* harbour/harbour.spec
* harbour/make_bsd.sh
* harbour/make_drw.sh
* harbour/make_gnu.sh
* harbour/make_rpm.sh
* harbour/make_tgz.sh
* harbour/bin/hb-func.sh
* harbour/bin/pack_src.sh
* harbour/config/c.cf
* harbour/config/global.cf
* harbour/config/rules.cf
* harbour/config/darwin/gcc.cf
* harbour/config/darwin/global.cf
* harbour/config/dos/djgpp.cf
* harbour/config/dos/global.cf
* harbour/config/dos/install.cf
* harbour/config/dos/owatcom.cf
* harbour/config/hpux/gcc.cf
* harbour/config/hpux/global.cf
* harbour/config/linux/gcc.cf
* harbour/config/linux/global.cf
* harbour/config/linux/owatcom.cf
* harbour/config/sunos/gcc.cf
* harbour/config/w32/watcom.cf
* include ADSRDD by default in RPMs
* updated for new RPM which does not accept some old tags
* set -fPIC on 64bit platforms
+ added /etc/harbour/hb-charmap.def
* updated for new GT system and drivers
* harbour/contrib/dot/pp.prg
* harbour/contrib/dot/pp_harb.ch
* use _APMAIN as startup function
* cleaned direct access to item internals
* harbour/contrib/libct/Makefile
+ harbour/contrib/libct/ctwfunc.c
+ harbour/contrib/libct/ctwin.c
+ harbour/contrib/libct/ctwin.h
* added CT3 like Window System - it's a GT driver which inherits
from the existing one and adds CTW functionality
* harbour/contrib/libct/screen1.c
* updated for GTAPI modifications
* harbour/contrib/libnf/Makefile
* harbour/contrib/libnf/chdir.c
* harbour/contrib/libnf/mkdir.c
* harbour/contrib/libnf/rmdir.c
* use hb_fs*() API functions instead of calling DOS interrupts
Now NF dir functions works on all platforms - it will be nice
to update other functions too.
* harbour/contrib/libnf/dispc.c
* harbour/contrib/libnf/ftattr.c
* updated for GT API modifications, some of this code still depends
on EGA/VGA video buffer so will work only if user will force in GT
using it - it will be nice to rewrite them
* harbour/contrib/odbc/odbc.c
* harbour/contrib/ole/ole2.c
* casting and cleaning direct access to item internals
* harbour/contrib/rdd_ads/ads1.c
* harbour/contrib/rdd_ads/adsfunc.c
* harbour/contrib/rdd_ads/adsmgmnt.c
* synced with xHarbour
* harbour/include/Makefile
* added new header files
+ harbour/include/hbgtinfo.ch
+ added GTI_* defintions for hb_gtInfo() function.
This function works in similar way to dbInfo() in RDD.
The GTI_* definitions are taken from xHarbour "as is" and
they should be cleaned - not all functionality are supported
in Harbour and some others should be implemented in differ
way then in xHarbour.
+ harbour/include/hbgtcore.h
* harbour/include/hbapigt.h
* new GTAPI
hbgtcore.h file should not be included by user code
it's only for internal use in GT drivers
* HB_inkey_enum changed to int - this is bit field not enumerated type.
Many of C/C++ compilers forbid bit operations on enum types and
forcing it by casting which finally exceeds the enum range is defined
as bug because it may badly interacts with some compiler optimizations
* harbour/include/hbapi.h
* harbour/include/hbapicdp.h
* harbour/include/hbapierr.h
* harbour/include/hbapifs.h
* harbour/include/hbapiitm.h
* harbour/include/hbapilng.h
* harbour/include/hbapirdd.h
* harbour/include/hbdate.h
* harbour/include/hbdefs.h
* harbour/include/hbinit.h
* harbour/include/hbpcode.h
* harbour/include/hbrdddbf.h
* harbour/include/hbset.h
* harbour/include/hbstack.h
* harbour/include/hbvm.h
* harbour/include/hbvmopt.h
* harbour/include/hbvmpub.h
* separated internal and external API. Now the definitions for
internal HVM structures and functions are excluded by default
they could be enabled if user include hbvmopt.h before other
header files. Such operation should be done _ONLY_ by core
code - if 3-rd party developers make sth like that then it's
for their own risk and such code may stop to work with next
Harbour versions. Without hbvmopt.h the internal structures
like HB_ITEM, HB_DYNS, ... are mapped to 'void' so there is
no way to access their members so we can modify them in the
future without afford for 3-rd party code.
There is one small exception 'type' should be the first member
for HB_ITEM structure because I used a small ugly hack in
HB_IS_*() macros with castin PHB_ITEM to HB_TYPE* - it works
without speed overhead but if you think that it will be
better/cleaner to not use such tricks then it's enough to
change HB_ITEM_TYPE() definitions in hbvmpub.h - see note.
* cleared the usage of HB_EXPORT - to avoid problems with some C/C++
compilers we agreed that the only one common way of using HB_EXPORT
is adding it ad begining of declaration - please keep this convention
in the future.
+ added new functions:
hb_extIsObject(), hb_codeblockId(), hb_idleSleep(),
hb_fsGetOsHandle(),
hb_dynsymFindSymbol(), hb_dynsymGetSymbol(),
hb_dynsymSymbol(), hb_dynsymName(),
hb_dynsymMemvarHandle(), hb_dynsymAreaHandle(), hb_dynsymSetAreaHandle()
* changed hb_arrayClone() declaration to:
PHB_ITEM hb_arrayClone( PHB_ITEM pArray )
* changed hb_arrayFromParams() declaration to:
PHB_ITEM hb_arrayFromParams( int iLevel )
The previous version needed a pointer to stack relocatable area
so any stack resizing could cause GPF.
* harbour/include/inkey.ch
* added definitions for extended mouse keys/events and some key
combinations
* harbour/source/codepage/uc1250.c
* harbour/source/codepage/uc1251.c
* harbour/source/codepage/uc1253.c
* harbour/source/codepage/uc1257.c
* harbour/source/codepage/uc737.c
* harbour/source/codepage/uc850.c
* harbour/source/codepage/uc852.c
* harbour/source/codepage/uc866.c
* harbour/source/codepage/uc88591b.c
* harbour/source/codepage/uc8859_1.c
* harbour/source/codepage/uc8859_2.c
* harbour/source/codepage/uc8859_5.c
* harbour/source/codepage/uckoi8.c
* harbour/source/codepage/uckoi8u.c
* harbour/source/codepage/ucmaz.c
* updated unicode values for characters in rabge 1-31 to keep
DOS compatibility
* harbour/source/common/expropt1.c
* harbour/source/common/hbarch.c
* harbour/source/common/hbdate.c
* harbour/source/common/hbstr.c
* harbour/source/common/hbver.c
* keep HB_EXPORT at the beginning of function declaration
* harbour/include/hbpcode.h
* harbour/include/hbcomp.h
* harbour/source/compiler/harbour.c
* harbour/source/compiler/harbour.y
* harbour/source/compiler/hbfix.c
+ harbour/source/compiler/hbdead.c
* harbour/source/compiler/hbpcode.c
+ harbour/source/compiler/hbstripl.c
% rewritten jump optimization
! fixed some minor problems
* do not optimize jumps and local variable access by shorter
PCODE version and HB_P_NOOP when jump optimization is
disabled, this is a note I left in source code in few places:
/*
* optimizing jumps here by shorting them and setting HB_P_NOOPs
* only slow down the compilation process for three reasons:
* 1. When it's dummy jump to next instruction we need two passes
* in hb_compOptimizeJumps() to fully remove it
* 2. hb_compOptimizeJumps() also make jump shortcutting in each pass
* 3. When Jump Optimization is disabled (-kJ) then it cause slowness
* at runtime because we will have more HVM loops: first for the
* shorter jump and next for the HB_P_NOOP PCODE(s)
* [druzuz]
*/
+ added support for multi passes in jump/dead code elimination
(hb_compOptimizeJumps())
By default is set upto three passes.
Now hb_compOptimizeJumps() keeps all compiler internal data clean
on exist and can be called any times and does not change other
compiler's functions behaviors
+ added dummy jumps elimination
+ added optimization for:
IF .T.
IF .F.
WHILE .T.
WHILE .F.
etc.
* restored empty BEGIN/RECOVER sequence block elimination
if Jump Optimization is enabled then it marks the block
with HB_P_NOOPS else it cut the generated PCODE
+ added dead code eliminator (new functions hb_compCodeTraceMarkDead()/
hb_compPCodeTrace() - it works only when Jump Optimization is
not disabled)
As a result of the above we have smaller and faster PCODE.
I do not think that we will have meta code support in the reasonable
time and because I need some valid compiler data/structures like
updated table of all jumps for real C code (not PCODE in .c files)
generation then I decide to make some modifications and the above
is in practice a side effect of this work.
* harbour/source/rdd/dbcmd.c
* harbour/source/rdd/dbf1.c
* harbour/source/rdd/rddord.prg
* harbour/source/rdd/workarea.c
* harbour/source/rdd/dbfcdx/dbfcdx1.c
* harbour/source/rdd/dbffpt/dbffpt1.c
* harbour/source/rdd/dbfntx/dbfntx1.c
* harbour/source/rdd/nulsys/nulsys.c
* synced with my modifications in xHarbour
* updated for API modifications
* harbour/source/rtl/Makefile
+ added gtsys.c, gtfunc.c, hbgtcore.c
* harbour/source/rtl/accept.c
* harbour/source/rtl/alert.prg
* harbour/source/rtl/cdpapi.c
* harbour/source/rtl/console.c
* harbour/source/rtl/dates.c
* harbour/source/rtl/do.c
* harbour/source/rtl/errorapi.c
* harbour/source/rtl/errorsys.prg
* harbour/source/rtl/file.c
* harbour/source/rtl/fserror.c
* harbour/source/rtl/fstemp.c
* harbour/source/rtl/hbffind.c
* harbour/source/rtl/idle.c
* harbour/source/rtl/math.c
* harbour/source/rtl/oldclear.c
* harbour/source/rtl/strmatch.c
* harbour/source/rtl/strpeek.c
* harbour/source/rtl/valtype.c
* harbour/source/rtl/xsavescr.c
* updated for API modifications
* cleaned some compiler warnings
* harbour/source/rtl/filesys.c
+ added hb_fsGetOsHandle()
* updated for API modifications
* harbour/source/rtl/gt.c
* harbour/source/rtl/gtapi.c
* harbour/source/rtl/gtapiu.c
+ harbour/source/rtl/gtfunc.c
+ harbour/source/rtl/gtsys.c
+ harbour/source/rtl/hbgtcore.c
* harbour/source/rtl/inkey.c
* harbour/source/rtl/maxrow.c
* harbour/source/rtl/mouseapi.c
* harbour/source/rtl/setcolor.c
* harbour/source/rtl/setposbs.c
* harbour/source/rtl/shadow.c
* new GT API code
* harbour/source/rtl/saverest.c
* changed default behavior in SEVESCREEN/RESTSCREEN with parameters
out of screen range to be Clipper compatible. Added last logical
parameter to keep previous behavior for programs which begins to
use it.
* harbour/source/rtl/seconds.c
* include missing header file to fix C++ compilation
* make hb_secondsCPU global function
* harbour/source/rtl/set.c
* make HB_SET_TYPEAHEAD Clipper compatible
* harbour/source/rtl/gtcgi/Makefile
* harbour/source/rtl/gtcgi/gtcgi.c
- harbour/source/rtl/gtcgi/mousecgi.c
* rewritten for new GT API
some detail behaviors has been changed but I think the current
implementation is better for CGI programs output - please check
and fix me if necessary
* harbour/source/rtl/gtcrs/Makefile
- harbour/source/rtl/gtcrs/charmap.prg
+ harbour/source/rtl/gtcrs/chrmap.c
- harbour/source/rtl/gtcrs/debug.map
- harbour/source/rtl/gtcrs/eterm.map
* harbour/source/rtl/gtcrs/gtcrs.c
+ harbour/source/rtl/gtcrs/gtcrs.h
+ harbour/source/rtl/gtcrs/hb-charmap.def
- harbour/source/rtl/gtcrs/kbdcrs.c
- harbour/source/rtl/gtcrs/keymap.prg
- harbour/source/rtl/gtcrs/linux.map
- harbour/source/rtl/gtcrs/mousecrs.c
* new GTCRS based on my xHarbour and Flagship curses code
Please not that at runtime it looks for a file
/etc/harbour/hb-charmap.def where user can fully tune output
for his terminal. This file is included with proper path in
binaries created by make_rpm.sh and make_tgz.sh
Ryszard I've removed some of your extensions which are no longer
necessary and some other (keyboard sequence redefinition) should
be done in a little bit differ way. I would like to talk about it
when you test current code.
Added support for extended mouse keys (middle button and wheel).
* harbour/source/rtl/gtdos/Makefile
* harbour/source/rtl/gtdos/gtdos.c
- harbour/source/rtl/gtdos/mousedos.c
* rewritten for new GT API
Fixed some small problems, finished mouse code which for DJGPP
is fully Clipper compatible with real mouse SAVE/RESTORE code.
For other compilers it should be updated depending on memory
model and used DPMI driver (if any).
* harbour/source/rtl/gtos2/Makefile
* harbour/source/rtl/gtos2/gtos2.c
- harbour/source/rtl/gtos2/mouseos2.c
* rewritten for new GT API
Fixed some problems and finished the mouse code.
Please test it - I made all modifications without OS2 and
I was not able to make any test. I'm interesting in information
if it works and the speed difference - f.e. results from
tests/vidtest.prg run with previous and current version.
* harbour/source/rtl/gtpca/Makefile
* harbour/source/rtl/gtpca/gtpca.c
- harbour/source/rtl/gtpca/kbdos2.gcc
- harbour/source/rtl/gtpca/mousepca.c
* rewritten for new GT API
and finished so now it's full functional GT driver
I'm interesting in keyboard sequences used by PC-ANSI drivers
in DOS so it will be possible to implement also support for
extended keys input.
* harbour/source/rtl/gtsln/Makefile
* harbour/source/rtl/gtsln/gtsln.c
+ harbour/source/rtl/gtsln/gtsln.h
* harbour/source/rtl/gtsln/kbsln.c
* harbour/source/rtl/gtsln/keytrans.c
* harbour/source/rtl/gtsln/mousesln.c
* rewritten for new GT API basing on current xHarbour code
added support for slang 1.4x patched for UNICODE (Debian
patches used by most of current Linux distributions) and
slang 2.x - It's unicode ready, tries to detect terminal mode
(utf-8/iso) at startup and switch the internal logic to
iso/unicode mode. When compiled with slang 1.4x or 2.x
and terminal is in UTF-8 it can display all characters like
in DOS if only used font have them or good fall-back table is
loaded (f.e. the one created by QRCZAK)
Added support for extended mouse keys (middle button and wheel).
* harbour/source/rtl/gtstd/Makefile
* harbour/source/rtl/gtstd/gtstd.c
- harbour/source/rtl/gtstd/mousestd.c
* rewritten for new GT API
Now it can work as full screen GT driver redrawing the previous
screen contents from internal GT core buffers. I run with this
GT some of my programs and they work quite well ;-) of course
without colors.
* harbour/source/rtl/gtwin/Makefile
* harbour/source/rtl/gtwin/gtwin.c
- harbour/source/rtl/gtwin/mousewin.c
* updated for new GT API
+ harbour/source/rtl/gtxwc/Makefile
+ harbour/source/rtl/gtxwc/gtxwc.c
+ harbour/source/rtl/gtxwc/gtxwc.h
* new XWindow Console GT driver based on my and Giancarlo Niccolai
code form xHarbour - this GT can work in XWindow environment only
and create its own window for console output. It delays the
initialization to the moment when user try to display anything on
the screen so even without X Window system programs which uses this
GT can work as long as use only outstd/outerr output.
* harbour/source/vm/arrays.c
* updated for API modifications
+ added hb_arrayId(),
* changed hb_arrayClone() declaration to:
HB_EXPORT PHB_ITEM hb_arrayClone( PHB_ITEM pSrcArray )
* changed hb_arrayFromParams() declaration to:
PHB_ITEM hb_arrayFromParams( int iLevel )
* harbour/source/vm/arrayshb.c
* updated for API modifications
+ added new parameter iLevel to function HB_APARAMS()
* harbour/source/vm/classes.c
* harbour/source/vm/cmdarg.c
* harbour/source/vm/dynlibhb.c
* harbour/source/vm/estack.c
* harbour/source/vm/fm.c
* harbour/source/vm/garbage.c
* harbour/source/vm/maindll.c
* harbour/source/vm/maindllh.c
* harbour/source/vm/maindllp.c
* harbour/source/vm/mainstd.c
* harbour/source/vm/mainwin.c
* harbour/source/vm/memvars.c
* harbour/source/vm/pcount.c
* harbour/source/vm/proc.c
* updated for API modifications
* harbour/source/vm/codebloc.c
* updated for API modifications
+ added hb_codeblockId()
* harbour/source/vm/dynsym.c
* updated for API modifications
+ added new functions:
hb_dynsymFindSymbol(), hb_dynsymGetSymbol(),
hb_dynsymSymbol(), hb_dynsymName(),
hb_dynsymMemvarHandle(), hb_dynsymAreaHandle(), hb_dynsymSetAreaHandle()
* harbour/source/vm/eval.c
* updated for API modifications
* call hb_vmPushState()/hb_vmPopState() in hb_itemDo()/hb_itemDoC()
functions - it's necessary to make HVM reentrant safe.
* harbour/source/vm/extend.c
* updated for API modifications
+ added hb_extIsObject() similar to existing hb_extIsArray()
* harbour/source/vm/hvm.c
* updated for API modifications
+ added new functions: hb_vmPushState(), hb_vmPopState()
which save/restore HVM state (the top stack value which can be
processed and return item and maybe sth else in the future) making
HVM ready for reentrant.
* harbour/source/vm/itemapi.c
* updated for API modifications
+ added new function:
HB_EXPORT PHB_SYMB hb_itemGetSymbol( PHB_ITEM pItem );
! fixed bug in hb_itemPutNInt()
* harbour/tests/Makefile
* harbour/utils/hbdoc/Makefile
* harbour/utils/hbextern/Makefile
* harbour/utils/hbmake/Makefile
* harbour/utils/hbrun/Makefile
* harbour/utils/hbtest/Makefile
- removed badly added: dbfntx, dbfcdx, dbffpt, hbsix libraries.
what broke GNU make compilation for some compilers
This libraries should be included automatically when RDD lib
is included by *.cf files
Summary:
The whole patch (cvs diff -uN) is ~1.5MB length and I cannot describe
everything in details - sorry but it was too much modifications in
one commit so now just some of general notes.
The header files included as is does not have any information about
internal HVM structures and some functions. In practice only HB_SYMB
is public and it has to be public for .c files generated from .prg
so I force the fixed size of this structure (alignment independent)
by redefining some members to union with void * - it's a little bit
ugly trick but it effectively eliminates the problem of linking
binaries compiled with differ alignment C compiler switches.
The structures like HB_ITEM, HB_CODEBLOCK, HB_STACK, HB_DYNS are not
longer defined and pointers to the defined as void * - like in Clipper
the ITEM structure. It caused that I had to add some new functions
to make some operation still possible to implement. If I missed sth
and any of you will have a problem with your code then please inform
me about it and after a small discussion on Harbour developers list
we can decide if other functions should be added.
The internal API is still accessible. It's enough to include "hbvmopt.h"
file before other header files to enable it. Now only files in
source/vm directory include it.
The 3-rd party code which does not include hbvmopt.h (or tries to
set some internals macros) should be safe for future HVM modifications
and will work also with new binaries so 3-rd party library developers
should remember about it. If they won't then it's only their and
their clients problem not Harbour developers.
With this modifications I also create new GT model which is similar
to the one used by RDD with multi inheritance. It can be quite easy
extended to simultaneously load more then one GT subsystem (sth like
work areas in RDD) but I left it for the future when someone may
need it. hbapigt.h file now contains only information about public
functions and does not have any code which depends on current
internal implementation - it should be backword compatible as long
as somone did not try to use internal GT functions.
The internal GT system use hbgtcore.h file which should not be
included by 3-rd party code as long as someone will not make new
GT driver. The internal GT code is new so I expect that it will
be changed yet in the nearest future (I'm waiting for other developers
feedback) and in such case any 3-rd party GTs will have to be updated.
How it works:
The base GT driver (GTNUL) is fully functional GT driver which
makes all operations on memory buffer. After each screen write
Flush() method is called which check for dispcount() and if
it's 0 then call Refresh() method to update modified area by
Redraw() method. This method is dummy in GTNUL and external
output with this GT can be reached by outstd()/outerr() which
are now redirected to GT methods. A simple GT driver may overload
only Refresh() method to give full screen output.
Application can use only this GT driver and it seems to be very
good choice for GUI and background daemon/service programs.
This GT is loaded at startup then all other GTs can be loaded
later and inherit from the previously loaded GT drivers.
The new GT driver can overload as much method as wants/needs.
Now all GT operations are implemented as GT method so GT driver
can easy change their default behavior, f.e. it may fully overload
color parsing methods and use differ or extended to Clipper color
definitions.
I rewrote all existing GTs to work with new GT model.
OS2 users - please test GTOS2 which I was not able to test and
see the note in Redraw() method. If possible please make some
speed tests.
In fact now there is much more internal operations then it was
before but because they are done on memory only then current
code is much faster in this GT drivers which so far makes all
operations on real video area. I made some tests with GTNUL
and tests/vidtest.prg and the total overhead is minimal. Now
dipbegin()/dispend() in practice does not cost anything so it
gives additional speed improvement in application which extensively
use it.
More then one GT driver can be linked with final binaries and
chose on application startup by //GT<NAME> switch and/or environment
variable HB_GT=<name>
In the RTL is new GT function GTSYS() which works in similar way
to RDDSYS() in RDD subsystem and for linking default GT driver
for given platform. When RTL is compiled the default GT driver
is set to HB_GT_DEFAULT envvar and if not exist to HB_GT_LIB
envvar and if it also does not exist to hard coded platfom GTs
(see source/rtl/gtsys.c and source/rtl/Makefile for rules)
Adding to source code:
ANNOUNCE GTSYS
disable linking the default GT driver and:
REQUEST HB_GT_<name>
for linking given (<name>) GT driver, f.e.: REQUEST HB_GT_WIN
If you are working in SH environment (Linux and other *nixes users,
DJGPP bash, MinGW shell then it's possible to use -gt<name> switch
in hblnk / hbmk scripts to force linking GT drivers (it could be
repeated with different <name>) and the first one becomes the default
one. F.e.:
xhbmk -m -n -w -es2 -gtcrs -gtsln -gtstd -gtpca vidtest.prg
Usually the GT driver are loaded at HVM startup but it's possible
to load it later. I created new GT driver CTW which gives full
CT3 like Window system. The CT3 extended driver change the behavior
of some function in Clipper extended driver, f.e WRITECON() or
SETPOS() with parameters out of screen range. So I implemented it
CTW as RT GTs which is loaded when some of CTWIN function is used
(f.e. WOPEN()/WBOARD()) and inherits from any existing GT driver.
This is full CT3 WIN implementation with all detail behaviors I
found (with some CT3 bug fixes). It does not have any CT3 limitations
and can be used for any virtual screen/window size though I hardcoded
CT3 limitation for backword compatibility. If somone will want to
remove it then it will be enough to delete few lines from ctwin.c
file. I'm not CTWIN Clipper user so maybe I missed some side effects
in this driver and was not able to well test it so if you will find
any incompatibilities then please inform me.
THe default GT buffer uses 32bit character cell internally but
in savescreen/restscreen it uses VGA compatible two bytes character
cell. Some GT drivers may want to use differ character cell.
Now GTCRS and GTSLN use 32bit character cell by default. They
need additional information about character set (box/normal) to
properly display box characters. It is possible to force in this
GT drivers using Clipper compatible character cell by calling:
hb_gtInfo( GTI_COMPATBUFFER, <lCompat> ) -> <lPreviousSeting>
but in may cause that box drawing characters will be lost in
some countries after RESTSCREEN(). It will depend on used code
page.
HB_GTINFO() is new function which works in similar way to DBINFO()
in RDD. It allows to retrieve/change some of GT driver settings.
GTI_* actions are defined in hbgtinfo.ch - it has all GTI_*
definitions used in xHarbour. Now in Harbour only few of them
are implemented.
The new three .prg functions:
HB_SETKEYCP( <cTermCP> [,<cHostCP>] )
HB_SETDISPCP( <cTermCP> [,<cHostCP>] [,<lBoxChar>] )
HB_SETTERMCP( <cTermCP> [,<cHostCP>] [,<lBoxChar>] )
have been added. They set automatic input (HB_SETKEYCP)
and output (HB_SETDISPCP) (or both: HB_SETTERMCP) character
translation. They are also important for some GTs which
informing them about used internal code page for unicode
translation (GTXWC, GTSLN) and/or chosing proper character
set (standard/alternate) for letters and other (f.e. box
drawing characters) (GTCRS, GTSLN),
<cTermCP> is encoding used on external (terminal) side
<cHostCP> is encoding used internally, if not given then
current code page set HB_SETCODEPAGE() is used.
some of GTs which uses unicode output may
ignore <cTermCP>
<lBoxChar> is optional parameter which interacts with dispbox()
output disabling switching to alternate character
set in some GTs. It effectively causes that if internal
(host) code page contains some letters on the box char
positions then they will be shown also by box drawing
functions like dispbox() instead of CP437 characters.
In some cases it could be useful. By default lBoxChar
is not set and GTs which can switch between standard
and alternate character set (GTCRS, GTSLN) will try to
use alternate character set for box drawing functions.
Victor: I removed some of your functions. They can be very easy
implemented with hb_gt_GetChar()/hb_gt_PutChar() but I do not want to
make them part of documented external API because some GT drivers may
want to use absolutely differ color definitions and they will stop to
work so I do not want to make this functions documented external API.
Ryszard: Setting alternative debug keys does not longer work.
I like such possibilities but it should be implemented in differ
way to f.e. using HB_GTINFO interface to allow low level GT driver
extensions, f.e. in *nixes using CTRL+[A-Z] and SIGINT, SIGQUIT,
SIGTSTP signals for real asynchronous setting of debug/cancel flag
without keyboard polling from main HVM loop. I would like to discus
about such more general solution.
*** Please updated non GNU make files ***
7654 lines
217 KiB
C
7654 lines
217 KiB
C
/*
|
|
* $Id$
|
|
*/
|
|
|
|
/*
|
|
* Harbour Project source code:
|
|
* DBFNTX RDD
|
|
*
|
|
* Copyright 1999 Bruno Cantero <bruno@issnet.net>
|
|
* www - http://www.harbour-project.org
|
|
*
|
|
* 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, 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 software; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
|
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
|
|
*
|
|
* As a special exception, the Harbour Project gives permission for
|
|
* additional uses of the text contained in its release of Harbour.
|
|
*
|
|
* The exception is that, if you link the Harbour libraries with other
|
|
* files to produce an executable, this does not by itself cause the
|
|
* resulting executable to be covered by the GNU General Public License.
|
|
* Your use of that executable is in no way restricted on account of
|
|
* linking the Harbour library code into it.
|
|
*
|
|
* This exception does not however invalidate any other reasons why
|
|
* the executable file might be covered by the GNU General Public License.
|
|
*
|
|
* This exception applies only to the code released by the Harbour
|
|
* Project under the name Harbour. If you copy code from other
|
|
* Harbour Project or Free Software Foundation releases into a copy of
|
|
* Harbour, as the General Public License permits, the exception does
|
|
* not apply to the code that you add in this way. To avoid misleading
|
|
* anyone as to the status of such modified files, you must delete
|
|
* this exception notice from them.
|
|
*
|
|
* If you write modifications of your own for Harbour, it is your choice
|
|
* whether to permit this exception to apply to your modifications.
|
|
* If you do not wish that, delete this exception notice.
|
|
*
|
|
*/
|
|
/*
|
|
* The following functions are added by
|
|
* Alexander Kresin <alex@belacy.belgorod.su>
|
|
*
|
|
* commonError()
|
|
* hb_IncString()
|
|
* ntxNumToStr()
|
|
* checkLogicalExpr()
|
|
* hb__ntxTagKeyCount()
|
|
* hb_ntxInTopScope()
|
|
* hb_ntxInBottomScope()
|
|
* hb_ntxTagKeyNo()
|
|
* hb_ntxTagKeyCount()
|
|
* hb_ntxClearScope()
|
|
* hb_ntxGoEof()
|
|
* hb_ntxGetKeyType()
|
|
* hb_ntxTagKeyFind()
|
|
* hb_ntxPageKeySearch()
|
|
* hb_ntxTagFindCurrentKey()
|
|
* hb_ntxIsRecBad()
|
|
* hb_ntxPageFindCurrentKey()
|
|
* hb_ntxGetCurrentKey()
|
|
* hb_ntxTagGoToNextKey()
|
|
* hb_ntxTagGoToPrevKey()
|
|
* hb_ntxTagGoToTopKey()
|
|
* hb_ntxTagGoToBottomKey()
|
|
* hb_ntxTagKeyGoTo()
|
|
* hb_ntxPageRelease()
|
|
* hb_ntxKeysMove()
|
|
* hb_ntxPageSplit()
|
|
* hb_ntxPageJoin()
|
|
* hb_ntxPageBalance()
|
|
* hb_ntxTagBalance()
|
|
* hb_ntxPageKeyDel()
|
|
* hb_ntxTagKeyAdd()
|
|
* hb_ntxSwapPageSave()
|
|
* hb_ntxKeysSort()
|
|
* hb_ntxSortKeyAdd()
|
|
* hb_ntxSortKeyEnd()
|
|
* hb_ntxWritePage()
|
|
* hb_ntxRootPage()
|
|
* hb_ntxGetSortedKey()
|
|
* hb_ntxBufferSave()
|
|
* hb_ntxReadBuf()
|
|
* hb_ntxPageFind()
|
|
* ntxFindIndex()
|
|
* hb_ntxOrdKeyAdd()
|
|
* hb_ntxOrdKeyDel()
|
|
* ntxGoBottom()
|
|
* ntxGoTo()
|
|
* ntxGoTop()
|
|
* ntxSeek()
|
|
* ntxSkipRaw()
|
|
* ntxGoCold()
|
|
* ntxGoHot()
|
|
* ntxSysName()
|
|
* ntxPack()
|
|
* ntxZap()
|
|
* ntxClearScope()
|
|
* ntxScopeInfo()
|
|
* ntxOrderListAdd()
|
|
* ntxOrderListClear()
|
|
* ntxOrderListFocus()
|
|
* ntxOrderListRebuild()
|
|
* ntxSetScope()
|
|
*/
|
|
|
|
/*
|
|
* Copyright 2005 Przemyslaw Czerpak <druzus@priv.onet.pl>
|
|
* in practice most of the code rewritten
|
|
*/
|
|
|
|
/* #define HB_NTX_NOMULTITAG */
|
|
|
|
/* #define HB_NTX_EXTERNAL_PAGEBUFFER */
|
|
|
|
#define HB_NTX_STRONG_BALANCE
|
|
|
|
/*
|
|
#define HB_NTX_DEBUG
|
|
#define HB_NTX_DEBUG_EXT
|
|
#define HB_NTX_DEBUG_DISP
|
|
*/
|
|
|
|
#include "hbapi.h"
|
|
#include "hbapiitm.h"
|
|
#include "hbinit.h"
|
|
#include "hbapierr.h"
|
|
#include "hbapilng.h"
|
|
#include "hbvm.h"
|
|
#include "hbset.h"
|
|
#include "hbmath.h"
|
|
#include "hbrddntx.h"
|
|
#include "rddsys.ch"
|
|
#ifdef __XHARBOUR__
|
|
#include "hbregex.h"
|
|
#endif
|
|
#ifndef HB_CDP_SUPPORT_OFF
|
|
#include "hbapicdp.h"
|
|
#endif
|
|
|
|
#ifdef HB_NTX_DEBUG_DISP
|
|
static ULONG s_rdNO = 0;
|
|
static ULONG s_wrNO = 0;
|
|
#endif
|
|
static RDDFUNCS ntxSuper;
|
|
static USHORT s_uiRddId;
|
|
|
|
static BOOL s_fSortRecNo = FALSE;
|
|
static BOOL s_fMultiKey = FALSE;
|
|
static BOOL s_fStruct = FALSE;
|
|
static BOOL s_fStrictStruct = FALSE;
|
|
#if !defined( HB_NTX_NOMULTITAG )
|
|
static BOOL s_fMultiTag = TRUE;
|
|
#endif
|
|
|
|
#define hb_ntxKeyFree(K) hb_xfree(K)
|
|
#define hb_ntxFileOffset(I,B) ( (B) << ( (I)->LargeFile ? NTXBLOCKBITS : 0 ) )
|
|
#define hb_ntxPageBuffer(p) ( (p)->buffer )
|
|
|
|
/*
|
|
* The helper functions (endian dependent) - on big endian machines
|
|
* or RISC with strict alignment it's much better to use functions
|
|
* then macros to inform compiler that can count complex parameters
|
|
* only once.
|
|
* On other machines it should not cause noticeable differences because
|
|
* most of modern C compilers auto inline small functions
|
|
*/
|
|
#if defined( HB_LITTLE_ENDIAN ) && !defined( HB_STRICT_ALIGNMENT )
|
|
|
|
#define hb_ntxGetKeyCount(p) HB_GET_LE_UINT16( hb_ntxPageBuffer(p) )
|
|
#define hb_ntxSetKeyCount(p,n) HB_PUT_LE_UINT16( hb_ntxPageBuffer(p), (n) )
|
|
|
|
#define hb_ntxGetKeyOffset(p,n) HB_GET_LE_UINT16( hb_ntxPageBuffer(p)+2+((n)<<1) )
|
|
#define hb_ntxGetKeyPtr(p,n) ( hb_ntxPageBuffer(p) + hb_ntxGetKeyOffset(p,n) )
|
|
#define hb_ntxGetKeyPage(p,n) HB_GET_LE_UINT32( hb_ntxGetKeyPtr(p,n) )
|
|
#define hb_ntxGetKeyRec(p,n) HB_GET_LE_UINT32( hb_ntxGetKeyPtr(p,n)+4 )
|
|
#define hb_ntxGetKeyVal(p,n) ( hb_ntxGetKeyPtr(p,n)+8 )
|
|
|
|
#define hb_ntxSetKeyOffset(p,n,u) HB_PUT_LE_UINT16( hb_ntxPageBuffer(p)+2+((n)<<1), u )
|
|
#define hb_ntxSetKeyPage(p,n,l) HB_PUT_LE_UINT32( hb_ntxGetKeyPtr(p,n), l )
|
|
#define hb_ntxSetKeyRec(p,n,l) HB_PUT_LE_UINT32( hb_ntxGetKeyPtr(p,n)+4, l )
|
|
|
|
#else
|
|
|
|
static USHORT hb_ntxGetKeyCount( LPPAGEINFO pPage )
|
|
{
|
|
char * ptr = hb_ntxPageBuffer( pPage );
|
|
return HB_GET_LE_UINT16( ptr );
|
|
}
|
|
|
|
static void hb_ntxSetKeyCount( LPPAGEINFO pPage, USHORT uiKeys )
|
|
{
|
|
char * ptr = hb_ntxPageBuffer( pPage );
|
|
HB_PUT_LE_UINT16( ptr, uiKeys );
|
|
}
|
|
|
|
static USHORT hb_ntxGetKeyOffset( LPPAGEINFO pPage, SHORT iKey )
|
|
{
|
|
char * ptr = hb_ntxPageBuffer( pPage ) + 2 + ( iKey << 1 );
|
|
return HB_GET_LE_UINT16( ptr );
|
|
}
|
|
|
|
static void hb_ntxSetKeyOffset( LPPAGEINFO pPage, SHORT iKey, USHORT uiOffset )
|
|
{
|
|
char * ptr = hb_ntxPageBuffer( pPage ) + 2 + ( iKey << 1 );
|
|
HB_PUT_LE_UINT16( ptr, uiOffset );
|
|
}
|
|
|
|
static char * hb_ntxGetKeyPtr( LPPAGEINFO pPage, SHORT iKey )
|
|
{
|
|
return hb_ntxPageBuffer( pPage ) + hb_ntxGetKeyOffset( pPage, iKey );
|
|
}
|
|
|
|
static ULONG hb_ntxGetKeyPage( LPPAGEINFO pPage, SHORT iKey )
|
|
{
|
|
char * ptr = hb_ntxGetKeyPtr( pPage, iKey );
|
|
return HB_GET_LE_UINT32( ptr );
|
|
}
|
|
|
|
static void hb_ntxSetKeyPage( LPPAGEINFO pPage, SHORT iKey, ULONG ulPage )
|
|
{
|
|
char * ptr = hb_ntxGetKeyPtr( pPage, iKey );
|
|
HB_PUT_LE_UINT32( ptr, ulPage );
|
|
}
|
|
|
|
static char * hb_ntxGetKeyVal( LPPAGEINFO pPage, SHORT iKey )
|
|
{
|
|
return hb_ntxGetKeyPtr( pPage, iKey ) + 8;
|
|
}
|
|
|
|
static void hb_ntxSetKeyRec( LPPAGEINFO pPage, SHORT iKey, ULONG ulRec )
|
|
{
|
|
char * ptr = hb_ntxGetKeyPtr( pPage, iKey ) + 4;
|
|
HB_PUT_LE_UINT32( ptr, ulRec );
|
|
}
|
|
|
|
static ULONG hb_ntxGetKeyRec( LPPAGEINFO pPage, SHORT iKey )
|
|
{
|
|
char * ptr = hb_ntxGetKeyPtr( pPage, iKey ) + 4;
|
|
return HB_GET_LE_UINT32( ptr );
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* generate Run-Time error
|
|
*/
|
|
static ERRCODE hb_ntxErrorRT( NTXAREAP pArea, USHORT uiGenCode, USHORT uiSubCode, char * szFileName, USHORT uiOsCode, USHORT uiFlags )
|
|
{
|
|
PHB_ITEM pError;
|
|
ERRCODE iRet = FAILURE;
|
|
|
|
if( hb_vmRequestQuery() == 0 )
|
|
{
|
|
pError = hb_errNew();
|
|
hb_errPutGenCode( pError, uiGenCode );
|
|
hb_errPutSubCode( pError, uiSubCode );
|
|
hb_errPutOsCode( pError, uiOsCode );
|
|
hb_errPutDescription( pError, hb_langDGetErrorDesc( uiGenCode ) );
|
|
if( szFileName )
|
|
hb_errPutFileName( pError, szFileName );
|
|
if( uiFlags )
|
|
hb_errPutFlags( pError, uiFlags );
|
|
iRet = SELF_ERROR( ( AREAP ) pArea, pError );
|
|
hb_errRelease( pError );
|
|
}
|
|
return iRet;
|
|
}
|
|
|
|
/*
|
|
* convert numeric item into NTX key value
|
|
*/
|
|
static char * hb_ntxNumToStr( PHB_ITEM pItem, char* szBuffer, USHORT length, USHORT dec )
|
|
{
|
|
char *ptr = szBuffer;
|
|
|
|
hb_itemStrBuf( szBuffer, pItem, length, dec );
|
|
|
|
while( *ptr == ' ' )
|
|
*ptr++ = '0';
|
|
|
|
if( *ptr == '-' )
|
|
{
|
|
*ptr = '0';
|
|
for( ptr = &szBuffer[0]; *ptr; ptr++ )
|
|
{
|
|
if( *ptr >= '0' && *ptr <= '9' )
|
|
*ptr = (char) ( '0' - ( *ptr - '0' ) - 4 );
|
|
/*
|
|
* I intentionally used the above formula to avoid problems on
|
|
* non ASCII machines though many of other xHarbour codes is
|
|
* hard coded to ASCII values and should be fixed. Druzus.
|
|
*/
|
|
}
|
|
}
|
|
|
|
return szBuffer;
|
|
}
|
|
|
|
/*
|
|
* convert numeric NTX key value into item
|
|
*/
|
|
static PHB_ITEM hb_ntxStrToNum( PHB_ITEM pItem, char* szKeyVal, USHORT length, USHORT dec )
|
|
{
|
|
char szBuffer[ NTX_MAX_KEY + 1 ];
|
|
char *ptr = szKeyVal, *ptr2, c;
|
|
int iLen, iDec;
|
|
HB_LONG lValue;
|
|
double dValue;
|
|
|
|
HB_SYMBOL_UNUSED( dec );
|
|
|
|
if( *ptr == '0' - 4 ) /* negative number */
|
|
{
|
|
ptr2 = szBuffer;
|
|
while( ( c = *ptr++ ) != 0 )
|
|
{
|
|
if( c != '.' )
|
|
c = '0' - ( c - '0' + 4 );
|
|
*ptr2++ = c;
|
|
}
|
|
szBuffer[ 0 ] = '-';
|
|
*ptr2 = '\0';
|
|
ptr = szBuffer;
|
|
}
|
|
if( hb_valStrnToNum( ptr, length, &lValue, &dValue, &iDec, &iLen ) )
|
|
return hb_itemPutNDLen( pItem, dValue, iLen, iDec );
|
|
else
|
|
return hb_itemPutNIntLen( pItem, lValue, length );
|
|
}
|
|
|
|
/*
|
|
* create new index key
|
|
*/
|
|
static LPKEYINFO hb_ntxKeyNew( LPKEYINFO pKeyFrom, int keylen )
|
|
{
|
|
LPKEYINFO pKey;
|
|
|
|
pKey = ( LPKEYINFO ) hb_xgrab( sizeof( KEYINFO ) + keylen );
|
|
if( pKeyFrom )
|
|
{
|
|
memcpy( pKey->key, pKeyFrom->key, keylen + 1 );
|
|
pKey->Tag = pKeyFrom->Tag;
|
|
pKey->Xtra = pKeyFrom->Xtra;
|
|
}
|
|
else
|
|
{
|
|
pKey->key[ keylen ] = '\0';
|
|
pKey->Tag = pKey->Xtra = 0;
|
|
}
|
|
return pKey;
|
|
}
|
|
|
|
/*
|
|
* copy index key, if dst is null create new dst key else destroy dst
|
|
*/
|
|
static LPKEYINFO hb_ntxKeyCopy( LPKEYINFO pKeyDest, LPKEYINFO pKey, int keylen )
|
|
{
|
|
if( !pKeyDest )
|
|
pKeyDest = hb_ntxKeyNew( NULL, keylen );
|
|
|
|
memcpy( pKeyDest->key, pKey->key, keylen + 1 );
|
|
pKeyDest->Tag = pKey->Tag;
|
|
pKeyDest->Xtra = pKey->Xtra;
|
|
|
|
return pKeyDest;
|
|
}
|
|
|
|
/*
|
|
* get ntx key type for given item
|
|
*/
|
|
static BYTE hb_ntxItemType( PHB_ITEM pItem )
|
|
{
|
|
switch( hb_itemType( pItem ) )
|
|
{
|
|
case HB_IT_STRING:
|
|
case HB_IT_STRING | HB_IT_MEMO:
|
|
return 'C';
|
|
|
|
case HB_IT_INTEGER:
|
|
case HB_IT_LONG:
|
|
case HB_IT_DOUBLE:
|
|
return 'N';
|
|
|
|
case HB_IT_DATE:
|
|
return 'D';
|
|
|
|
case HB_IT_LOGICAL:
|
|
return 'L';
|
|
|
|
default:
|
|
return 'U';
|
|
}
|
|
}
|
|
|
|
/*
|
|
* store Item in index key
|
|
* TODO: uiType check and generate RT error if necessary
|
|
* probably not here or we will have to add parameter
|
|
* for scope key evaluation
|
|
*/
|
|
static LPKEYINFO hb_ntxKeyPutItem( LPKEYINFO pKey, PHB_ITEM pItem, ULONG ulRecNo,
|
|
LPTAGINFO pTag, BOOL fTrans, USHORT *puiLen )
|
|
{
|
|
ULONG len;
|
|
|
|
if( !pKey )
|
|
pKey = hb_ntxKeyNew( NULL, pTag->KeyLength );
|
|
|
|
if( puiLen )
|
|
*puiLen = pTag->KeyLength;
|
|
|
|
switch( hb_ntxItemType( pItem ) )
|
|
{
|
|
case 'C':
|
|
len = hb_itemGetCLen( pItem );
|
|
if( len < ( ULONG ) pTag->KeyLength )
|
|
{
|
|
memcpy( pKey->key, hb_itemGetCPtr( pItem ), len );
|
|
memset( pKey->key + len, ' ', pTag->KeyLength - len );
|
|
if( puiLen )
|
|
*puiLen = ( USHORT ) len;
|
|
}
|
|
else
|
|
{
|
|
memcpy( pKey->key, hb_itemGetCPtr( pItem ), pTag->KeyLength );
|
|
}
|
|
pKey->key[ pTag->KeyLength ] = '\0';
|
|
#ifndef HB_CDP_SUPPORT_OFF
|
|
if( fTrans )
|
|
hb_cdpnTranslate( pKey->key, hb_cdp_page, pTag->Owner->Owner->cdPage, pTag->KeyLength );
|
|
#else
|
|
HB_SYMBOL_UNUSED( fTrans );
|
|
#endif
|
|
break;
|
|
case 'N':
|
|
hb_ntxNumToStr( pItem, pKey->key, pTag->KeyLength, pTag->KeyDec );
|
|
break;
|
|
case 'D':
|
|
hb_itemGetDS( pItem, pKey->key );
|
|
break;
|
|
case 'L':
|
|
pKey->key[0] = ( hb_itemGetL( pItem ) ? 'T':'F' );
|
|
pKey->key[1] = 0;
|
|
break;
|
|
default:
|
|
memset( pKey->key, '\0', pTag->KeyLength );
|
|
}
|
|
pKey->Xtra = ulRecNo;
|
|
pKey->Tag = 0;
|
|
|
|
return pKey;
|
|
}
|
|
|
|
/*
|
|
* get Item from index key
|
|
*/
|
|
static PHB_ITEM hb_ntxKeyGetItem( PHB_ITEM pItem, LPKEYINFO pKey,
|
|
LPTAGINFO pTag, BOOL fTrans )
|
|
{
|
|
if( pKey )
|
|
{
|
|
switch( pTag->KeyType )
|
|
{
|
|
case 'C':
|
|
#ifndef HB_CDP_SUPPORT_OFF
|
|
if( fTrans && pTag->Owner->Owner->cdPage != hb_cdp_page )
|
|
{
|
|
char * pVal = ( char * ) hb_xgrab( pTag->KeyLength + 1 );
|
|
memcpy( pVal, pKey->key, pTag->KeyLength );
|
|
pVal[ pTag->KeyLength ] = '\0';
|
|
hb_cdpnTranslate( pVal, pTag->Owner->Owner->cdPage, hb_cdp_page,
|
|
pTag->KeyLength );
|
|
pItem = hb_itemPutCPtr( pItem, pVal, pTag->KeyLength );
|
|
}
|
|
else
|
|
#else
|
|
HB_SYMBOL_UNUSED( fTrans );
|
|
#endif
|
|
{
|
|
pItem = hb_itemPutCL( pItem, pKey->key, pTag->KeyLength );
|
|
}
|
|
break;
|
|
case 'N':
|
|
pItem = hb_ntxStrToNum( pItem, pKey->key, pTag->KeyLength, pTag->KeyDec );
|
|
break;
|
|
case 'D':
|
|
pItem = hb_itemPutDS( pItem, pKey->key );
|
|
break;
|
|
case 'L':
|
|
pItem = hb_itemPutL( pItem, pKey->key[0] == 'T' );
|
|
break;
|
|
default:
|
|
if( pItem )
|
|
hb_itemClear( pItem );
|
|
else
|
|
pItem = hb_itemNew( NULL );
|
|
}
|
|
}
|
|
else if( pItem )
|
|
hb_itemClear( pItem );
|
|
else
|
|
pItem = hb_itemNew( NULL );
|
|
|
|
return pItem;
|
|
}
|
|
|
|
/*
|
|
* evaluate conditional expression and return the logical result
|
|
*/
|
|
static BOOL hb_ntxEvalCond( NTXAREAP pArea, PHB_ITEM pCondItem, BOOL fSetWA )
|
|
{
|
|
int iCurrArea = 0;
|
|
BOOL fRet;
|
|
|
|
if( fSetWA )
|
|
{
|
|
iCurrArea = hb_rddGetCurrentWorkAreaNumber();
|
|
if( iCurrArea != pArea->uiArea )
|
|
hb_rddSelectWorkAreaNumber( pArea->uiArea );
|
|
else
|
|
iCurrArea = 0;
|
|
}
|
|
|
|
fRet = hb_itemGetL( hb_vmEvalBlockOrMacro( pCondItem ) );
|
|
|
|
if( iCurrArea )
|
|
hb_rddSelectWorkAreaNumber( iCurrArea );
|
|
|
|
return fRet;
|
|
}
|
|
|
|
/*
|
|
* evaluate seek/skip block: {|key, rec| ... }
|
|
*/
|
|
static BOOL hb_ntxEvalSeekCond( LPTAGINFO pTag, PHB_ITEM pCondItem )
|
|
{
|
|
BOOL fRet;
|
|
PHB_ITEM pKeyVal, pKeyRec;
|
|
|
|
pKeyVal = hb_ntxKeyGetItem( NULL, pTag->CurKeyInfo, pTag, TRUE );
|
|
pKeyRec = hb_itemPutNInt( NULL, pTag->CurKeyInfo->Xtra );
|
|
|
|
fRet = hb_itemGetL( hb_vmEvalBlockV( pCondItem, 2, pKeyVal, pKeyRec ) );
|
|
|
|
hb_itemRelease( pKeyVal );
|
|
hb_itemRelease( pKeyRec );
|
|
|
|
return fRet;
|
|
}
|
|
|
|
/*
|
|
* get ITEM type of key expression
|
|
*/
|
|
static BYTE hb_ntxGetKeyType( LPTAGINFO pTag )
|
|
{
|
|
BYTE bType;
|
|
|
|
if( pTag->nField )
|
|
{
|
|
PHB_ITEM pItem = hb_itemNew( NULL );
|
|
SELF_GETVALUE( ( AREAP ) pTag->Owner->Owner, pTag->nField, pItem );
|
|
bType = hb_ntxItemType( pItem );
|
|
hb_itemRelease( pItem );
|
|
}
|
|
else
|
|
{
|
|
int iCurrArea = hb_rddGetCurrentWorkAreaNumber();
|
|
|
|
if( iCurrArea != pTag->Owner->Owner->uiArea )
|
|
hb_rddSelectWorkAreaNumber( pTag->Owner->Owner->uiArea );
|
|
else
|
|
iCurrArea = 0;
|
|
|
|
bType = hb_ntxItemType( hb_vmEvalBlockOrMacro( pTag->pKeyItem ) );
|
|
|
|
if( iCurrArea )
|
|
hb_rddSelectWorkAreaNumber( iCurrArea );
|
|
}
|
|
return bType;
|
|
}
|
|
|
|
/*
|
|
* evaluate key expression and create new Key from the result
|
|
*/
|
|
static LPKEYINFO hb_ntxEvalKey( LPKEYINFO pKey, LPTAGINFO pTag )
|
|
{
|
|
NTXAREAP pArea = pTag->Owner->Owner;
|
|
PHB_ITEM pItem;
|
|
#ifndef HB_CDP_SUPPORT_OFF
|
|
/* TODO%: this hack is not thread safe, hb_cdp_page has to be thread specific */
|
|
PHB_CODEPAGE cdpTmp = hb_cdp_page;
|
|
hb_cdp_page = pArea->cdPage;
|
|
#endif
|
|
|
|
if( pTag->nField )
|
|
{
|
|
pItem = hb_itemNew( NULL );
|
|
SELF_GETVALUE( ( AREAP ) pArea, pTag->nField, pItem );
|
|
pKey = hb_ntxKeyPutItem( pKey, pItem, pArea->ulRecNo, pTag, FALSE, NULL );
|
|
hb_itemRelease( pItem );
|
|
}
|
|
else
|
|
{
|
|
int iCurrArea = hb_rddGetCurrentWorkAreaNumber();
|
|
|
|
if( iCurrArea != pArea->uiArea )
|
|
hb_rddSelectWorkAreaNumber( pArea->uiArea );
|
|
else
|
|
iCurrArea = 0;
|
|
|
|
pItem = hb_vmEvalBlockOrMacro( pTag->pKeyItem );
|
|
pKey = hb_ntxKeyPutItem( pKey, pItem, pArea->ulRecNo, pTag, FALSE, NULL );
|
|
|
|
if( iCurrArea )
|
|
hb_rddSelectWorkAreaNumber( iCurrArea );
|
|
}
|
|
|
|
#ifndef HB_CDP_SUPPORT_OFF
|
|
hb_cdp_page = cdpTmp;
|
|
#endif
|
|
|
|
return pKey;
|
|
}
|
|
|
|
/*
|
|
* compare two values using Tag conditions (len & type)
|
|
*/
|
|
static int hb_ntxValCompare( LPTAGINFO pTag, char* val1, int len1,
|
|
char* val2, int len2, BOOL fExact )
|
|
{
|
|
int iLimit, iResult = 0;
|
|
|
|
iLimit = (len1 > len2) ? len2 : len1;
|
|
|
|
if( pTag->KeyType == 'C' )
|
|
{
|
|
if( iLimit > 0 )
|
|
{
|
|
#ifndef HB_CDP_SUPPORT_OFF
|
|
if( pTag->Owner->Owner->cdPage->lSort )
|
|
iResult = hb_cdpcmp( val1, val2, ( ULONG ) iLimit, pTag->Owner->Owner->cdPage, NULL );
|
|
else
|
|
#endif
|
|
iResult = memcmp( val1, val2, iLimit );
|
|
}
|
|
|
|
if( iResult == 0 )
|
|
{
|
|
if( len1 > len2 )
|
|
iResult = 1;
|
|
else if( len1 < len2 && fExact )
|
|
iResult = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( iLimit <= 0 || (iResult = memcmp( val1, val2, iLimit )) == 0 )
|
|
{
|
|
if( len1 > len2 )
|
|
iResult = 1;
|
|
else if( len1 < len2 )
|
|
iResult = -1;
|
|
}
|
|
}
|
|
return iResult;
|
|
}
|
|
|
|
/*
|
|
* check if a given key is in top scope
|
|
*/
|
|
static BOOL hb_ntxInTopScope( LPTAGINFO pTag, char* key )
|
|
{
|
|
PHB_NTXSCOPE pScope = pTag->fUsrDescend ? &pTag->bottom : &pTag->top;
|
|
|
|
if( pScope->scopeKeyLen )
|
|
{
|
|
int i = hb_ntxValCompare( pTag, pScope->scopeKey->key, pScope->scopeKeyLen,
|
|
key, pTag->KeyLength, FALSE );
|
|
return pTag->fUsrDescend ? i >= 0 : i <= 0;
|
|
}
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* check if a given key is in bottom scope
|
|
*/
|
|
static BOOL hb_ntxInBottomScope( LPTAGINFO pTag, char* key )
|
|
{
|
|
PHB_NTXSCOPE pScope = pTag->fUsrDescend ? &pTag->top : &pTag->bottom;
|
|
|
|
if( pScope->scopeKeyLen )
|
|
{
|
|
int i = hb_ntxValCompare( pTag, pScope->scopeKey->key, pScope->scopeKeyLen,
|
|
key, pTag->KeyLength, FALSE );
|
|
return pTag->fUsrDescend ? i <= 0 : i >= 0;
|
|
}
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* check if a given key is in current scope
|
|
*/
|
|
static BOOL hb_ntxKeyInScope( LPTAGINFO pTag, LPKEYINFO pKey )
|
|
{
|
|
return hb_ntxInTopScope( pTag, pKey->key ) &&
|
|
hb_ntxInBottomScope( pTag, pKey->key );
|
|
}
|
|
|
|
/*
|
|
* clear top or bottom scope
|
|
*/
|
|
static void hb_ntxTagClearScope( LPTAGINFO pTag, USHORT nScope )
|
|
{
|
|
NTXAREAP pArea = pTag->Owner->Owner;
|
|
PHB_NTXSCOPE pScope;
|
|
|
|
/* resolve any pending scope relations first */
|
|
if( pArea->lpdbPendingRel && pArea->lpdbPendingRel->isScoped )
|
|
SELF_FORCEREL( ( AREAP ) pArea );
|
|
|
|
if( pTag->fUsrDescend )
|
|
nScope = ( nScope == 0 ) ? 1 : 0;
|
|
|
|
pScope = ( nScope == 0 ) ? &pTag->top : &pTag->bottom;
|
|
|
|
if( pScope->scopeKey )
|
|
{
|
|
hb_ntxKeyFree( pScope->scopeKey );
|
|
pScope->scopeKey = NULL;
|
|
}
|
|
if( pScope->scopeItem )
|
|
{
|
|
hb_itemRelease( pScope->scopeItem );
|
|
pScope->scopeItem = NULL;
|
|
}
|
|
pScope->scopeKeyLen = 0;
|
|
|
|
pTag->keyCount = 0;
|
|
}
|
|
|
|
/*
|
|
* set top or bottom scope
|
|
*/
|
|
static void hb_ntxTagSetScope( LPTAGINFO pTag, USHORT nScope, PHB_ITEM pItem )
|
|
{
|
|
NTXAREAP pArea = pTag->Owner->Owner;
|
|
PHB_ITEM pScopeVal;
|
|
|
|
/* resolve any pending scope relations first */
|
|
if( pArea->lpdbPendingRel && pArea->lpdbPendingRel->isScoped )
|
|
SELF_FORCEREL( ( AREAP ) pArea );
|
|
|
|
pScopeVal = ( hb_itemType( pItem ) == HB_IT_BLOCK ) ?
|
|
hb_vmEvalBlock( pItem ) : pItem;
|
|
|
|
if( pTag->KeyType == hb_ntxItemType( pScopeVal ) )
|
|
{
|
|
PHB_NTXSCOPE pScope;
|
|
BOOL fTop = ( nScope == 0 );
|
|
|
|
if( pTag->fUsrDescend )
|
|
fTop = !fTop;
|
|
|
|
pScope = fTop ? &pTag->top : &pTag->bottom;
|
|
|
|
pScope->scopeKey = hb_ntxKeyPutItem( pScope->scopeKey, pScopeVal,
|
|
( fTop == pTag->AscendKey ) ? NTX_IGNORE_REC_NUM : NTX_MAX_REC_NUM,
|
|
pTag, TRUE, &pScope->scopeKeyLen );
|
|
|
|
if( pScope->scopeItem == NULL )
|
|
pScope->scopeItem = hb_itemNew( NULL );
|
|
hb_itemCopy( pScope->scopeItem, pItem );
|
|
|
|
pTag->keyCount = 0;
|
|
}
|
|
else
|
|
{
|
|
hb_ntxTagClearScope( pTag, nScope );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* get top or bottom scope item
|
|
*/
|
|
static void hb_ntxTagGetScope( LPTAGINFO pTag, USHORT nScope, PHB_ITEM pItem )
|
|
{
|
|
NTXAREAP pArea = pTag->Owner->Owner;
|
|
PHB_NTXSCOPE pScope;
|
|
|
|
/* resolve any pending scope relations first */
|
|
if( pArea->lpdbPendingRel && pArea->lpdbPendingRel->isScoped )
|
|
SELF_FORCEREL( ( AREAP ) pArea );
|
|
|
|
if( pTag->fUsrDescend )
|
|
nScope = ( nScope == 0 ) ? 1 : 0;
|
|
|
|
pScope = ( nScope == 0 ) ? &pTag->top : &pTag->bottom;
|
|
|
|
if( pScope->scopeItem )
|
|
hb_itemCopy( pItem, pScope->scopeItem );
|
|
else
|
|
hb_itemClear( pItem );
|
|
}
|
|
|
|
/*
|
|
* refresh top and bottom scope value if set as codeblock
|
|
*/
|
|
static void hb_ntxTagRefreshScope( LPTAGINFO pTag )
|
|
{
|
|
PHB_ITEM pItem;
|
|
|
|
/* resolve any pending scope relations first */
|
|
if( pTag->Owner->Owner->lpdbPendingRel &&
|
|
pTag->Owner->Owner->lpdbPendingRel->isScoped )
|
|
SELF_FORCEREL( ( AREAP ) pTag->Owner->Owner );
|
|
|
|
if( hb_itemType( pTag->top.scopeItem ) == HB_IT_BLOCK )
|
|
{
|
|
pItem = hb_vmEvalBlock( pTag->top.scopeItem );
|
|
pTag->top.scopeKey = hb_ntxKeyPutItem( pTag->top.scopeKey, pItem,
|
|
pTag->top.scopeKey->Xtra, pTag, TRUE, &pTag->top.scopeKeyLen );
|
|
}
|
|
if( hb_itemType( pTag->bottom.scopeItem ) == HB_IT_BLOCK )
|
|
{
|
|
pItem = hb_vmEvalBlock( pTag->bottom.scopeItem );
|
|
pTag->bottom.scopeKey = hb_ntxKeyPutItem( pTag->bottom.scopeKey, pItem,
|
|
pTag->bottom.scopeKey->Xtra, pTag, TRUE, &pTag->bottom.scopeKeyLen );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* an interface for fast check record number in record filter
|
|
*/
|
|
static BOOL hb_ntxCheckRecordScope( NTXAREAP pArea, ULONG ulRec )
|
|
{
|
|
LONG lRecNo = ( LONG ) ulRec;
|
|
|
|
if ( SELF_COUNTSCOPE( ( AREAP ) pArea, NULL, &lRecNo ) == SUCCESS && lRecNo == 0 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef HB_NTX_DEBUG
|
|
static void hb_ntxTagCheckBuffers( LPTAGINFO pTag )
|
|
{
|
|
LPPAGEINFO pPage;
|
|
ULONG i;
|
|
|
|
if( ( pTag->HdrChanged || pTag->Owner->Changed ) && !pTag->Owner->lockWrite )
|
|
hb_errInternal( 9999, "hb_ntxTagCheckBuffers: tag modified in unlocked index", "", "" );
|
|
|
|
for( i = 0; i < pTag->Owner->ulPages; i++ )
|
|
{
|
|
pPage = pTag->Owner->pages[ i ];
|
|
if( pPage->Changed && !pTag->Owner->lockWrite )
|
|
hb_errInternal( 9999, "hb_ntxTagCheckBuffers: page modified in unlocked index", "", "" );
|
|
if( pPage->iUsed )
|
|
hb_errInternal( 9999, "hb_ntxTagCheckBuffers: page still allocated", "", "" );
|
|
}
|
|
}
|
|
|
|
static void hb_ntxPageCheckKeys( LPPAGEINFO pPage, LPTAGINFO pTag, int iPos, int iType )
|
|
{
|
|
USHORT u;
|
|
int i;
|
|
|
|
for( u = 1; u < pPage->uiKeys; u++ )
|
|
{
|
|
i = hb_ntxValCompare( pTag,
|
|
hb_ntxGetKeyVal( pPage, u - 1 ), pTag->KeyLength,
|
|
hb_ntxGetKeyVal( pPage, u ), pTag->KeyLength, TRUE );
|
|
if( !pTag->AscendKey )
|
|
i = -i;
|
|
if( i > 0 )
|
|
{
|
|
printf("\r\nuiKeys=%d(%d/%d), (%d)[%.*s]>(%d)[%.*s]", pPage->uiKeys, iPos, iType,
|
|
u - 1, pTag->KeyLength, hb_ntxGetKeyVal( pPage, u - 1 ),
|
|
u, pTag->KeyLength, hb_ntxGetKeyVal( pPage, u ) );
|
|
fflush(stdout);
|
|
hb_errInternal( 9999, "hb_ntxPageCheckKeys: keys sorted wrong.", "", "" );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* read a given block from index file
|
|
*/
|
|
static BOOL hb_ntxBlockRead( LPNTXINDEX pIndex, ULONG ulBlock, BYTE *buffer, int iSize )
|
|
{
|
|
if( !pIndex->lockRead && !pIndex->lockWrite )
|
|
hb_errInternal( 9103, "hb_ntxBlockRead on not locked index file.", "", "" );
|
|
|
|
#ifdef HB_NTX_DEBUG_DISP
|
|
s_rdNO++;
|
|
#endif
|
|
hb_fsSeekLarge( pIndex->DiskFile,
|
|
hb_ntxFileOffset( pIndex, ulBlock ), FS_SET );
|
|
if( hb_fsRead( pIndex->DiskFile, buffer, iSize ) != iSize )
|
|
{
|
|
hb_ntxErrorRT( pIndex->Owner, EG_READ, EDBF_READ,
|
|
pIndex->IndexName, hb_fsError(), 0 );
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* write a given block into index file
|
|
*/
|
|
static BOOL hb_ntxBlockWrite( LPNTXINDEX pIndex, ULONG ulBlock, BYTE *buffer, int iSize )
|
|
{
|
|
if( !pIndex->lockWrite )
|
|
hb_errInternal( 9102, "hb_ntxBlockWrite on not locked index file.", "", "" );
|
|
|
|
#ifdef HB_NTX_DEBUG_DISP
|
|
s_wrNO++;
|
|
#endif
|
|
hb_fsSeekLarge( pIndex->DiskFile,
|
|
hb_ntxFileOffset( pIndex, ulBlock ), FS_SET );
|
|
if( hb_fsWrite( pIndex->DiskFile, buffer, iSize ) != iSize )
|
|
{
|
|
hb_ntxErrorRT( pIndex->Owner, EG_WRITE, EDBF_WRITE,
|
|
pIndex->IndexName, hb_fsError(), 0 );
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* write a given tag page to file
|
|
*/
|
|
static BOOL hb_ntxPageSave( LPNTXINDEX pIndex, LPPAGEINFO pPage )
|
|
{
|
|
hb_ntxSetKeyCount( pPage, pPage->uiKeys );
|
|
if( !hb_ntxBlockWrite( pIndex, pPage->Page,
|
|
(BYTE *) hb_ntxPageBuffer( pPage ), NTXBLOCKSIZE ) )
|
|
return FALSE;
|
|
pPage->Changed = FALSE;
|
|
pIndex->fFlush = TRUE;
|
|
/* In shared mode we have to update counter in version field of
|
|
NTXHEADER to signal for other stations that their index buffers
|
|
has to be discarded */
|
|
if( pIndex->fShared )
|
|
pIndex->Changed = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* discard all index buffers due to concurrent access
|
|
*/
|
|
static void hb_ntxDiscardBuffers( LPNTXINDEX pIndex )
|
|
{
|
|
pIndex->ulPages = pIndex->ulPageLast = 0;
|
|
pIndex->pChanged = pIndex->pFirst = pIndex->pLast = NULL;
|
|
if( pIndex->Compound )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < pIndex->iTags; i++ )
|
|
{
|
|
pIndex->lpTags[ i ]->RootBlock = 0;
|
|
pIndex->lpTags[ i ]->stackLevel = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pIndex->TagBlock = 0;
|
|
if( pIndex->iTags )
|
|
pIndex->lpTags[ 0 ]->stackLevel = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* update tag flags
|
|
*/
|
|
static void hb_ntxTagUpdateFlags( LPTAGINFO pTag )
|
|
{
|
|
USHORT uiSignature = pTag->Signature;
|
|
|
|
pTag->Custom = ( uiSignature & NTX_FLAG_CUSTOM ) != 0;
|
|
pTag->ChgOnly = ( uiSignature & NTX_FLAG_CHGONLY ) != 0;
|
|
pTag->Partial = ( uiSignature & NTX_FLAG_PARTIAL ) != 0;
|
|
pTag->Template = ( uiSignature & NTX_FLAG_TEMPLATE ) != 0;
|
|
pTag->MultiKey = ( uiSignature & NTX_FLAG_MULTIKEY ) != 0;
|
|
pTag->fSortRec = ( uiSignature & NTX_FLAG_SORTRECNO ) != 0;
|
|
}
|
|
|
|
/*
|
|
* check tag header in compound index
|
|
*/
|
|
static BOOL hb_ntxTagHeaderCheck( LPTAGINFO pTag )
|
|
{
|
|
if( !pTag->RootBlock )
|
|
{
|
|
if( pTag->HeadBlock )
|
|
{
|
|
BYTE buffer[ 12 ];
|
|
if( hb_ntxBlockRead( pTag->Owner, pTag->HeadBlock, buffer, 12 ) )
|
|
{
|
|
pTag->Signature = HB_GET_LE_UINT16( ( ( LPNTXHEADER ) buffer )->type );
|
|
pTag->RootBlock = HB_GET_LE_UINT32( ( ( LPNTXHEADER ) buffer )->root );
|
|
hb_ntxTagUpdateFlags( pTag );
|
|
}
|
|
}
|
|
}
|
|
return pTag->RootBlock != 0;
|
|
}
|
|
|
|
/*
|
|
* free buffers for pages in the tag
|
|
*/
|
|
static void hb_ntxFreePageBuffer( LPNTXINDEX pIndex )
|
|
{
|
|
ULONG ul, ulMax = pIndex->ulPagesDepth;
|
|
LPPAGEINFO * pPagePtr = pIndex->pages;
|
|
|
|
if( ulMax )
|
|
{
|
|
for( ul = 0; ul < ulMax; ul++, pPagePtr++ )
|
|
{
|
|
if( *pPagePtr )
|
|
{
|
|
#ifdef HB_NTX_EXTERNAL_PAGEBUFFER
|
|
if( hb_ntxPageBuffer( *pPagePtr ) )
|
|
hb_xfree( hb_ntxPageBuffer( *pPagePtr ) );
|
|
#endif
|
|
hb_xfree( *pPagePtr );
|
|
}
|
|
}
|
|
hb_xfree( pIndex->pages );
|
|
pIndex->pages = NULL;
|
|
pIndex->ulPages = pIndex->ulPageLast = pIndex->ulPagesDepth = 0;
|
|
pIndex->pFirst = pIndex->pLast = pIndex->pChanged = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* trunc index file, left only space for header
|
|
*/
|
|
static void hb_ntxIndexTrunc( LPNTXINDEX pIndex )
|
|
{
|
|
if( !pIndex->lockWrite )
|
|
hb_errInternal( 9102, "hb_ntxIndexTrunc on not locked index file.", "", "" );
|
|
|
|
hb_ntxFreePageBuffer( pIndex );
|
|
pIndex->Update = pIndex->Changed = pIndex->fFlush = TRUE;
|
|
pIndex->TagBlock = pIndex->NextAvail = 0;
|
|
pIndex->Version = 0;
|
|
hb_fsSeek( pIndex->DiskFile, NTXBLOCKSIZE, FS_SET );
|
|
hb_fsWrite( pIndex->DiskFile, NULL, 0 );
|
|
}
|
|
|
|
/*
|
|
* try to find given tag page in the buffer
|
|
*/
|
|
static LPPAGEINFO hb_ntxPageFind( LPTAGINFO pTag, ULONG ulPage )
|
|
{
|
|
LPPAGEINFO * pPagePtr = pTag->Owner->pages;
|
|
ULONG u;
|
|
|
|
for( u = pTag->Owner->ulPages; u; u--, pPagePtr++ )
|
|
{
|
|
if( *pPagePtr && (*pPagePtr)->Page == ulPage )
|
|
return *pPagePtr;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* try to find free space in buffer
|
|
*/
|
|
static LPPAGEINFO hb_ntxPageGetBuffer( LPTAGINFO pTag, ULONG ulPage )
|
|
{
|
|
LPNTXINDEX pIndex = pTag->Owner;
|
|
LPPAGEINFO * pPagePtr;
|
|
|
|
if( pIndex->ulPages < pIndex->ulPagesDepth )
|
|
{
|
|
pPagePtr = &pIndex->pages[ pIndex->ulPages++ ];
|
|
}
|
|
else if( pIndex->pFirst )
|
|
{
|
|
LPPAGEINFO pPage = pIndex->pFirst;
|
|
|
|
if( pPage->iUsed )
|
|
hb_errInternal( 9999, "hb_ntxPageGetBuffer: page used.", "", "" );
|
|
if( pPage->Changed )
|
|
hb_errInternal( 9999, "hb_ntxPageGetBuffer: page changed.", "", "" );
|
|
|
|
pIndex->pFirst = pPage->pNext;
|
|
if( pIndex->pFirst )
|
|
pIndex->pFirst->pPrev = NULL;
|
|
else
|
|
pIndex->pLast = NULL;
|
|
pPage->pPrev = NULL;
|
|
pPage->Page = ulPage;
|
|
pPage->iUsed = 1;
|
|
|
|
return pPage;
|
|
}
|
|
else if( pIndex->ulPagesDepth == 0 )
|
|
{
|
|
pIndex->ulPages = 1;
|
|
pIndex->ulPageLast = 0;
|
|
pIndex->ulPagesDepth = NTX_PAGES_PER_TAG;
|
|
pIndex->pages = (LPPAGEINFO*) hb_xgrab( sizeof(LPPAGEINFO) * NTX_PAGES_PER_TAG );
|
|
memset( pIndex->pages, 0, sizeof(LPPAGEINFO) * NTX_PAGES_PER_TAG );
|
|
pPagePtr = &pIndex->pages[0];
|
|
}
|
|
else
|
|
{
|
|
ULONG ul = pIndex->ulPageLast;
|
|
do
|
|
{
|
|
if( ++ul >= pIndex->ulPagesDepth )
|
|
ul = 0;
|
|
pPagePtr = &pIndex->pages[ ul ];
|
|
if( !(*pPagePtr)->iUsed && !(*pPagePtr)->Changed )
|
|
{
|
|
pIndex->ulPageLast = ul;
|
|
break;
|
|
}
|
|
if( ul == pIndex->ulPageLast )
|
|
{
|
|
ul = pIndex->ulPagesDepth;
|
|
pIndex->ulPagesDepth += NTX_PAGES_PER_TAG >> 1;
|
|
pIndex->pages = (LPPAGEINFO*) hb_xrealloc( pIndex->pages,
|
|
sizeof(LPPAGEINFO) * pIndex->ulPagesDepth );
|
|
memset( pIndex->pages + ul, 0,
|
|
( NTX_PAGES_PER_TAG >> 1 ) * sizeof( LPPAGEINFO ) );
|
|
pIndex->ulPages++;
|
|
pPagePtr = &pIndex->pages[ ul ];
|
|
pIndex->ulPageLast = 0;
|
|
break;
|
|
}
|
|
}
|
|
while( TRUE );
|
|
}
|
|
|
|
if( !*pPagePtr )
|
|
{
|
|
*pPagePtr = ( LPPAGEINFO ) hb_xgrab( sizeof( HB_PAGEINFO ) );
|
|
memset( *pPagePtr, 0, sizeof( HB_PAGEINFO ) );
|
|
}
|
|
#ifdef HB_NTX_EXTERNAL_PAGEBUFFER
|
|
if( !hb_ntxPageBuffer( *pPagePtr ) )
|
|
{
|
|
hb_ntxPageBuffer( *pPagePtr ) = ( char* ) hb_xgrab( NTXBLOCKSIZE );
|
|
memset( hb_ntxPageBuffer( *pPagePtr ), 0, NTXBLOCKSIZE );
|
|
}
|
|
#endif
|
|
(*pPagePtr)->pPrev = NULL;
|
|
(*pPagePtr)->Page = ulPage;
|
|
(*pPagePtr)->iUsed = 1;
|
|
return *pPagePtr;
|
|
}
|
|
|
|
/*
|
|
* free the index page for future reuse
|
|
*/
|
|
static void hb_ntxPageFree( LPTAGINFO pTag, LPPAGEINFO pPage )
|
|
{
|
|
hb_ntxSetKeyPage( pPage, 0, pTag->Owner->NextAvail );
|
|
pTag->Owner->NextAvail = pPage->Page;
|
|
pTag->Owner->Changed = pPage->Changed = TRUE;
|
|
}
|
|
|
|
/*
|
|
* mark used page as free
|
|
*/
|
|
static void hb_ntxPageRelease( LPTAGINFO pTag, LPPAGEINFO pPage )
|
|
{
|
|
LPNTXINDEX pIndex = pTag->Owner;
|
|
|
|
if( --pPage->iUsed == 0 )
|
|
{
|
|
if( pPage->Changed )
|
|
{
|
|
if( !pPage->pPrev )
|
|
{
|
|
pPage->pPrev = pPage;
|
|
pPage->pNext = pIndex->pChanged;
|
|
pIndex->pChanged = pPage;
|
|
}
|
|
}
|
|
else if( pIndex->pLast )
|
|
{
|
|
pIndex->pLast->pNext = pPage;
|
|
pPage->pPrev = pIndex->pLast;
|
|
pPage->pNext = NULL;
|
|
pIndex->pLast = pPage;
|
|
}
|
|
else
|
|
{
|
|
pPage->pNext = pPage->pPrev = NULL;
|
|
pIndex->pFirst = pIndex->pLast = pPage;
|
|
}
|
|
}
|
|
else if( pPage->iUsed < 0 )
|
|
hb_errInternal( 9999, "hb_ntxPageRelease: unused page freed.", "", "" );
|
|
}
|
|
|
|
/*
|
|
* load page from index file or the buffer
|
|
*/
|
|
static LPPAGEINFO hb_ntxPageLoad( LPTAGINFO pTag, ULONG ulPage )
|
|
{
|
|
LPPAGEINFO pPage;
|
|
|
|
if( !ulPage )
|
|
{
|
|
if( hb_ntxTagHeaderCheck( pTag ) )
|
|
ulPage = pTag->RootBlock;
|
|
if( !ulPage )
|
|
{
|
|
hb_ntxErrorRT( pTag->Owner->Owner, EG_CORRUPTION, EDBF_CORRUPT,
|
|
pTag->Owner->IndexName, 0, 0 );
|
|
}
|
|
}
|
|
pPage = hb_ntxPageFind( pTag, ulPage );
|
|
if( pPage )
|
|
{
|
|
if( !pPage->Changed && !pPage->iUsed )
|
|
{
|
|
if( pPage->pNext )
|
|
pPage->pNext->pPrev = pPage->pPrev;
|
|
else
|
|
pTag->Owner->pLast = pPage->pPrev;
|
|
if( pPage->pPrev )
|
|
{
|
|
pPage->pPrev->pNext = pPage->pNext;
|
|
pPage->pPrev = NULL;
|
|
}
|
|
else
|
|
pTag->Owner->pFirst = pPage->pNext;
|
|
}
|
|
pPage->iUsed++;
|
|
}
|
|
else
|
|
{
|
|
pPage = hb_ntxPageGetBuffer( pTag, ulPage );
|
|
if( !hb_ntxBlockRead( pTag->Owner, ulPage,
|
|
(BYTE *) hb_ntxPageBuffer( pPage ), NTXBLOCKSIZE ) )
|
|
return NULL;
|
|
pPage->Changed = FALSE;
|
|
pPage->uiKeys = hb_ntxGetKeyCount( pPage );
|
|
}
|
|
return pPage;
|
|
}
|
|
|
|
/*
|
|
* initialize empty page structure
|
|
*/
|
|
static void hb_ntxPageInit( LPTAGINFO pTag, LPPAGEINFO pPage )
|
|
{
|
|
USHORT u, o = ( pTag->MaxKeys + 2 ) << 1;
|
|
|
|
for( u = 0; u <= pTag->MaxKeys; u++, o += pTag->KeyLength + 8 )
|
|
hb_ntxSetKeyOffset( pPage, u, o );
|
|
hb_ntxSetKeyPage( pPage, 0, 0 );
|
|
pPage->uiKeys = 0;
|
|
}
|
|
|
|
/*
|
|
* allocate new page address
|
|
*/
|
|
static ULONG hb_ntxPageAlloc( LPNTXINDEX pIndex )
|
|
{
|
|
ULONG ulPage;
|
|
if( !pIndex->TagBlock )
|
|
{
|
|
HB_FOFFSET fOffset;
|
|
fOffset = hb_fsSeekLarge( pIndex->DiskFile, 0, FS_END );
|
|
pIndex->TagBlock = ( ULONG )
|
|
( fOffset >> ( pIndex->LargeFile ? NTXBLOCKBITS : 0 ) );
|
|
}
|
|
ulPage = pIndex->TagBlock;
|
|
pIndex->TagBlock += pIndex->LargeFile ? 1 : NTXBLOCKSIZE;
|
|
return ulPage;
|
|
}
|
|
|
|
/*
|
|
* allocate new page in index file - reuse freed one or increase file
|
|
*/
|
|
static LPPAGEINFO hb_ntxPageNew( LPTAGINFO pTag, BOOL fNull )
|
|
{
|
|
LPPAGEINFO pPage;
|
|
|
|
if( pTag->Owner->NextAvail != 0 )
|
|
{
|
|
/* Handling of a pool of empty pages.
|
|
Some sources says that this address is in the first 4 bytes of
|
|
a page ( http://www.e-bachmann.dk/docs/xbase.htm ).
|
|
But as I understood, studying dumps of Clipper ntx'es, address of the
|
|
next available page is in the address field of a first key item
|
|
in the page - it is done here now in such a way.
|
|
= Alexander Kresin =
|
|
*/
|
|
pPage = hb_ntxPageLoad( pTag, pTag->Owner->NextAvail );
|
|
if( ! pPage )
|
|
return NULL;
|
|
if( pPage->uiKeys != 0 )
|
|
{
|
|
hb_ntxErrorRT( pTag->Owner->Owner, EG_CORRUPTION, EDBF_CORRUPT,
|
|
pTag->Owner->IndexName, 0, 0 );
|
|
return NULL;
|
|
}
|
|
pTag->Owner->NextAvail = hb_ntxGetKeyPage( pPage, 0 );
|
|
#if defined( HB_NTX_NOMULTITAG )
|
|
hb_ntxSetKeyPage( pPage, 0, 0 );
|
|
pPage->uiKeys = 0;
|
|
#else
|
|
hb_ntxPageInit( pTag, pPage );
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
pPage = hb_ntxPageGetBuffer( pTag, fNull ? 0 : hb_ntxPageAlloc( pTag->Owner ) );
|
|
hb_ntxPageInit( pTag, pPage );
|
|
}
|
|
pTag->Owner->Changed = pPage->Changed = TRUE;
|
|
|
|
return pPage;
|
|
}
|
|
|
|
/*
|
|
* add given page to list of free pages
|
|
*/
|
|
static void hb_ntxPageAddFree( LPTAGINFO pTag, ULONG ulPage )
|
|
{
|
|
LPPAGEINFO pPage = hb_ntxPageGetBuffer( pTag, ulPage );
|
|
pPage->Changed = TRUE;
|
|
hb_ntxPageInit( pTag, pPage );
|
|
hb_ntxPageFree( pTag, pPage );
|
|
hb_ntxPageSave( pTag->Owner, pPage );
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
}
|
|
|
|
/*
|
|
* get free page in index file
|
|
*/
|
|
static ULONG hb_ntxPageGetFree( LPTAGINFO pTag )
|
|
{
|
|
LPPAGEINFO pPage = hb_ntxPageNew( pTag, FALSE );
|
|
ULONG ulPage = 0;
|
|
|
|
if( pPage )
|
|
{
|
|
ulPage = pPage->Page;
|
|
pPage->Changed = FALSE;
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
}
|
|
return ulPage;
|
|
}
|
|
|
|
/*
|
|
* create the new tag structure
|
|
*/
|
|
static LPTAGINFO hb_ntxTagNew( LPNTXINDEX pIndex,
|
|
char * szTagName, BOOL fTagName,
|
|
char *szKeyExpr, PHB_ITEM pKeyExpr,
|
|
BYTE bKeyType, USHORT uiKeyLen, USHORT uiKeyDec,
|
|
char *szForExpr, PHB_ITEM pForExpr,
|
|
BOOL fAscendKey, BOOL fUnique, BOOL fCustom,
|
|
BOOL fSortRec )
|
|
{
|
|
LPTAGINFO pTag;
|
|
|
|
pTag = ( LPTAGINFO ) hb_xgrab( sizeof( TAGINFO ) );
|
|
memset( pTag, 0, sizeof( TAGINFO ) );
|
|
pTag->TagName = hb_strndup( szTagName, NTX_MAX_TAGNAME );
|
|
pTag->fTagName = fTagName;
|
|
pTag->Owner = pIndex;
|
|
if( szKeyExpr )
|
|
{
|
|
pTag->KeyExpr = hb_strndup( szKeyExpr, NTX_MAX_EXP );
|
|
}
|
|
if( pForExpr && szForExpr )
|
|
{
|
|
pTag->ForExpr = hb_strndup( szForExpr, NTX_MAX_EXP );
|
|
}
|
|
pTag->nField = hb_rddFieldExpIndex( ( AREAP ) pIndex->Owner, pTag->KeyExpr );
|
|
pTag->pKeyItem = pKeyExpr;
|
|
pTag->pForItem = pForExpr;
|
|
pTag->AscendKey = fAscendKey;
|
|
pTag->fUsrDescend = !pTag->AscendKey;
|
|
pTag->UniqueKey = fUnique;
|
|
pTag->Custom = fCustom;
|
|
pTag->MultiKey = fCustom && s_fMultiKey;
|
|
pTag->KeyType = bKeyType;
|
|
pTag->KeyLength = uiKeyLen;
|
|
pTag->KeyDec = uiKeyDec;
|
|
pTag->fSortRec = fSortRec;
|
|
/*
|
|
* TODO?: keep during page update the offset to 'MaxKeys' key fixed
|
|
* so we will be able to store 1 key more in the page
|
|
*/
|
|
pTag->MaxKeys = ( NTXBLOCKSIZE - 2 ) / ( uiKeyLen + 10 ) - 1;
|
|
|
|
/* TODO?: is it necessary? It should not interact with well implemented
|
|
algorithm */
|
|
if( pTag->MaxKeys & 0x01 && pTag->MaxKeys > 2 )
|
|
pTag->MaxKeys--;
|
|
|
|
pTag->CurKeyInfo = hb_ntxKeyNew( NULL, pTag->KeyLength );
|
|
|
|
return pTag;
|
|
}
|
|
|
|
/*
|
|
* free from memory tag structure
|
|
*/
|
|
static void hb_ntxTagFree( LPTAGINFO pTag )
|
|
{
|
|
if( pTag == pTag->Owner->Owner->lpCurTag )
|
|
pTag->Owner->Owner->lpCurTag = NULL;
|
|
hb_xfree( pTag->TagName );
|
|
if( pTag->KeyExpr )
|
|
hb_xfree( pTag->KeyExpr );
|
|
if( pTag->ForExpr )
|
|
hb_xfree( pTag->ForExpr );
|
|
if( pTag->pKeyItem )
|
|
hb_vmDestroyBlockOrMacro( pTag->pKeyItem );
|
|
if( pTag->pForItem )
|
|
hb_vmDestroyBlockOrMacro( pTag->pForItem );
|
|
if( pTag->HotKeyInfo )
|
|
hb_ntxKeyFree( pTag->HotKeyInfo );
|
|
hb_ntxKeyFree( pTag->CurKeyInfo );
|
|
hb_ntxTagClearScope( pTag, 0 );
|
|
hb_ntxTagClearScope( pTag, 1 );
|
|
if( pTag->stack )
|
|
hb_xfree( pTag->stack );
|
|
hb_xfree( pTag );
|
|
}
|
|
|
|
/*
|
|
* delete tag from compund index
|
|
*/
|
|
static void hb_ntxTagDelete( LPTAGINFO pTag )
|
|
{
|
|
LPNTXINDEX pIndex = pTag->Owner;
|
|
int i;
|
|
|
|
for( i = 0; i < pIndex->iTags; i++ )
|
|
{
|
|
if( pTag == pIndex->lpTags[ i ] )
|
|
{
|
|
while( ++i < pIndex->iTags )
|
|
pIndex->lpTags[ i - 1 ] = pIndex->lpTags[ i ];
|
|
if( --pIndex->iTags )
|
|
pIndex->lpTags = ( LPTAGINFO * ) hb_xrealloc( pIndex->lpTags,
|
|
sizeof( LPTAGINFO ) * pIndex->iTags );
|
|
else
|
|
hb_xfree( pIndex->lpTags );
|
|
break;
|
|
}
|
|
}
|
|
hb_ntxTagFree( pTag );
|
|
pIndex->Owner->fSetTagNumbers = TRUE;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* add tag to compund index
|
|
*/
|
|
static ERRCODE hb_ntxTagAdd( LPNTXINDEX pIndex, LPTAGINFO pTag )
|
|
{
|
|
if( pIndex->iTags >= CTX_MAX_TAGS )
|
|
return FAILURE;
|
|
|
|
if( pIndex->iTags )
|
|
pIndex->lpTags = ( LPTAGINFO * ) hb_xrealloc( pIndex->lpTags,
|
|
sizeof( LPTAGINFO ) * ( pIndex->iTags + 1 ) );
|
|
else
|
|
pIndex->lpTags = ( LPTAGINFO * ) hb_xgrab( sizeof( LPTAGINFO ) );
|
|
|
|
pIndex->lpTags[ pIndex->iTags++ ] = pTag;
|
|
pIndex->Owner->fSetTagNumbers = TRUE;
|
|
return SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* create new tag and load it from index file
|
|
*/
|
|
static LPTAGINFO hb_ntxTagLoad( LPNTXINDEX pIndex, ULONG ulBlock,
|
|
char * szTagName, BYTE * buffer )
|
|
{
|
|
LPNTXHEADER lpNTX = ( LPNTXHEADER ) buffer;
|
|
LPTAGINFO pTag;
|
|
PHB_ITEM pKeyExp, pForExp = NULL;
|
|
USHORT usType;
|
|
BOOL fName;
|
|
|
|
usType = HB_GET_LE_UINT16( lpNTX->type );
|
|
|
|
if( ( usType & ~NTX_FLAG_MASK ) ||
|
|
( ( usType & NTX_FLAG_DEFALUT ) != NTX_FLAG_DEFALUT &&
|
|
usType != NTX_FLAG_OLDDEFALUT ) ||
|
|
lpNTX->key_expr[0] < 0x20 )
|
|
return NULL;
|
|
|
|
if( SELF_COMPILE( ( AREAP ) pIndex->Owner, lpNTX->key_expr ) == FAILURE )
|
|
return NULL;
|
|
pKeyExp = pIndex->Owner->valResult;
|
|
pIndex->Owner->valResult = NULL;
|
|
|
|
if( usType & NTX_FLAG_FORITEM && lpNTX->for_expr[0] >= 0x20 )
|
|
{
|
|
if( SELF_COMPILE( ( AREAP ) pIndex->Owner, lpNTX->for_expr ) == FAILURE )
|
|
{
|
|
hb_vmDestroyBlockOrMacro( pKeyExp );
|
|
return NULL;
|
|
}
|
|
pForExp = pIndex->Owner->valResult;
|
|
pIndex->Owner->valResult = NULL;
|
|
}
|
|
fName = !pIndex->Compound && lpNTX->tag_name[0] >= 0x20;
|
|
pTag = hb_ntxTagNew( pIndex,
|
|
fName ? (char *) lpNTX->tag_name : szTagName, fName,
|
|
(char *) lpNTX->key_expr, pKeyExp,
|
|
'\0',
|
|
HB_GET_LE_UINT16( lpNTX->key_size ),
|
|
HB_GET_LE_UINT16( lpNTX->key_dec ),
|
|
(char *) lpNTX->for_expr, pForExp,
|
|
lpNTX->descend[0] == 0, lpNTX->unique[0] != 0,
|
|
( usType & NTX_FLAG_CUSTOM ) != 0 || lpNTX->custom[0] != 0,
|
|
( usType & NTX_FLAG_SORTRECNO ) != 0 );
|
|
|
|
pTag->Signature = usType;
|
|
hb_ntxTagUpdateFlags( pTag );
|
|
pTag->HeadBlock = ulBlock;
|
|
pTag->RootBlock = HB_GET_LE_UINT32( lpNTX->root );
|
|
pTag->MaxKeys = HB_GET_LE_UINT16( lpNTX->max_item );
|
|
pTag->KeyType = hb_ntxGetKeyType( pTag );
|
|
|
|
pIndex->LargeFile = ( usType & NTX_FLAG_LARGEFILE ) != 0;
|
|
|
|
if( !pIndex->Compound )
|
|
{
|
|
pIndex->Version = HB_GET_LE_UINT16( lpNTX->version );
|
|
pIndex->NextAvail = HB_GET_LE_UINT32( lpNTX->next_page );
|
|
pIndex->TagBlock = 0;
|
|
|
|
/* TODO: this breaks unlocking !!! */
|
|
if( usType & NTX_FLAG_LARGEFILE )
|
|
{
|
|
pIndex->Owner->bLockType = DB_DBFLOCK_XHB64;
|
|
}
|
|
else if( usType & NTX_FLAG_EXTLOCK )
|
|
{
|
|
pIndex->Owner->bLockType = DB_DBFLOCK_CL53EXT;
|
|
}
|
|
else if( ! pIndex->Owner->bLockType )
|
|
{
|
|
pIndex->Owner->bLockType = usType & NTX_FLAG_EXTLOCK ?
|
|
DB_DBFLOCK_CL53EXT : DB_DBFLOCK_CLIP;
|
|
}
|
|
}
|
|
return pTag;
|
|
}
|
|
|
|
/*
|
|
* add tag into CTX header
|
|
*/
|
|
static void hb_ntxIndexTagAdd( LPNTXINDEX pIndex, LPTAGINFO pTag )
|
|
{
|
|
LPCTXHEADER lpCTX = ( LPCTXHEADER ) pIndex->HeaderBuff;
|
|
int iTags = HB_GET_LE_UINT16( lpCTX->ntags ), i;
|
|
LPCTXTAGITEM pTagItem = ( LPCTXTAGITEM ) lpCTX->tags;
|
|
|
|
for( i = 0; i < iTags; pTagItem++, i++ )
|
|
{
|
|
if( !hb_strnicmp( ( char * ) pTagItem->tag_name, pTag->TagName, NTX_MAX_TAGNAME ) )
|
|
break;
|
|
}
|
|
if( i == iTags )
|
|
{
|
|
++iTags;
|
|
HB_PUT_LE_UINT16( lpCTX->ntags, iTags );
|
|
strncpy( ( char * ) pTagItem->tag_name, pTag->TagName, NTX_MAX_TAGNAME );
|
|
}
|
|
HB_PUT_LE_UINT32( pTagItem->tag_header, pTag->HeadBlock );
|
|
pIndex->Update = TRUE;
|
|
}
|
|
|
|
/*
|
|
* delete tag from CTX header
|
|
*/
|
|
static void hb_ntxIndexTagDel( LPNTXINDEX pIndex, char * szTagName )
|
|
{
|
|
LPCTXHEADER lpCTX = ( LPCTXHEADER ) pIndex->HeaderBuff;
|
|
int iTags = HB_GET_LE_UINT16( lpCTX->ntags ), i;
|
|
LPCTXTAGITEM pTagItem = ( LPCTXTAGITEM ) lpCTX->tags;
|
|
|
|
for( i = 0; i < iTags; pTagItem++, i++ )
|
|
{
|
|
if( !hb_strnicmp( ( char * ) pTagItem->tag_name, szTagName, NTX_MAX_TAGNAME ) )
|
|
{
|
|
memmove( pTagItem, pTagItem + 1, ( iTags - i ) * NTX_TAGITEMSIZE );
|
|
memset( pTagItem + iTags - 1, 0, NTX_TAGITEMSIZE );
|
|
--iTags;
|
|
HB_PUT_LE_UINT16( lpCTX->ntags, iTags );
|
|
pIndex->Update = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* find tag header block in CTX header
|
|
*/
|
|
static ULONG hb_ntxIndexTagFind( LPCTXHEADER lpCTX, char * szTagName )
|
|
{
|
|
int iTags = HB_GET_LE_UINT16( lpCTX->ntags ), i;
|
|
LPCTXTAGITEM pTagItem = ( LPCTXTAGITEM ) lpCTX->tags;
|
|
|
|
for( i = 0; i < iTags; pTagItem++, i++ )
|
|
{
|
|
if( !hb_strnicmp( ( char * ) pTagItem->tag_name, szTagName, NTX_MAX_TAGNAME ) )
|
|
return HB_GET_LE_UINT32( pTagItem->tag_header );
|
|
}
|
|
return NTX_DUMMYNODE;
|
|
}
|
|
|
|
/*
|
|
* Write tag header
|
|
*/
|
|
static ERRCODE hb_ntxTagHeaderSave( LPTAGINFO pTag )
|
|
{
|
|
LPNTXINDEX pIndex = pTag->Owner;
|
|
NTXHEADER Header;
|
|
int iSize = 12, type, version = 0;
|
|
ULONG next = 0;
|
|
|
|
if( pIndex->Compound )
|
|
{
|
|
if( !pTag->HeadBlock )
|
|
{
|
|
pTag->HeadBlock = hb_ntxPageGetFree( pTag );
|
|
if( !pTag->HeadBlock )
|
|
return FAILURE;
|
|
hb_ntxIndexTagAdd( pIndex, pTag );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( pTag->HeadBlock )
|
|
{
|
|
hb_ntxPageAddFree( pTag, pTag->HeadBlock );
|
|
pTag->HeadBlock = 0;
|
|
pIndex->Update = TRUE;
|
|
}
|
|
pIndex->Version++;
|
|
version = pIndex->Version &= 0xffff;
|
|
next = pIndex->NextAvail;
|
|
}
|
|
|
|
type = NTX_FLAG_DEFALUT |
|
|
( pTag->ForExpr ? NTX_FLAG_FORITEM : 0 ) |
|
|
( pTag->Partial ? NTX_FLAG_PARTIAL | NTX_FLAG_FORITEM : 0 ) |
|
|
( pIndex->Owner->bLockType == DB_DBFLOCK_CL53EXT ? NTX_FLAG_EXTLOCK : 0 ) |
|
|
( pTag->Partial ? NTX_FLAG_PARTIAL | NTX_FLAG_FORITEM : 0 ) |
|
|
/* non CLipper flags */
|
|
( pTag->Custom ? NTX_FLAG_CUSTOM : 0 ) |
|
|
( pTag->ChgOnly ? NTX_FLAG_CHGONLY : 0 ) |
|
|
( pTag->Template ? NTX_FLAG_TEMPLATE : 0 ) |
|
|
( pTag->MultiKey ? NTX_FLAG_MULTIKEY : 0 ) |
|
|
( pTag->fSortRec ? NTX_FLAG_SORTRECNO : 0 ) |
|
|
( pIndex->LargeFile ? NTX_FLAG_LARGEFILE : 0 );
|
|
|
|
HB_PUT_LE_UINT16( Header.type, type );
|
|
HB_PUT_LE_UINT16( Header.version, version );
|
|
HB_PUT_LE_UINT32( Header.root, pTag->RootBlock );
|
|
HB_PUT_LE_UINT32( Header.next_page, next );
|
|
|
|
if( pIndex->Update )
|
|
{
|
|
memset( ( BYTE * ) &Header + 12, 0, sizeof( NTXHEADER ) - 12 );
|
|
|
|
HB_PUT_LE_UINT16( Header.item_size, pTag->KeyLength + 8 );
|
|
HB_PUT_LE_UINT16( Header.key_size, pTag->KeyLength );
|
|
HB_PUT_LE_UINT16( Header.key_dec, pTag->KeyDec );
|
|
HB_PUT_LE_UINT16( Header.max_item, pTag->MaxKeys );
|
|
HB_PUT_LE_UINT16( Header.half_page, pTag->MaxKeys >> 1 );
|
|
Header.unique[0] = pTag->UniqueKey ? 1 : 0;
|
|
Header.descend[0] = pTag->AscendKey ? 0 : 1;
|
|
Header.custom[0] = pTag->Custom ? 1 : 0;
|
|
strncpy( ( char * ) Header.key_expr, pTag->KeyExpr, NTX_MAX_EXP );
|
|
if( pTag->ForExpr )
|
|
strncpy( ( char * ) Header.for_expr, pTag->ForExpr, NTX_MAX_EXP );
|
|
if( pTag->fTagName )
|
|
strncpy( ( char * ) Header.tag_name, pTag->TagName, NTX_MAX_TAGNAME );
|
|
iSize = sizeof( NTXHEADER );
|
|
}
|
|
|
|
if( !hb_ntxBlockWrite( pIndex, pTag->HeadBlock, ( BYTE * ) &Header, iSize ) )
|
|
return FAILURE;
|
|
pTag->HdrChanged = FALSE;
|
|
pIndex->Changed = pIndex->Compound;
|
|
pIndex->fFlush = TRUE;
|
|
return SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* create new index structure
|
|
*/
|
|
static LPNTXINDEX hb_ntxIndexNew( NTXAREAP pArea )
|
|
{
|
|
LPNTXINDEX pIndex;
|
|
|
|
pIndex = ( LPNTXINDEX ) hb_xgrab( sizeof( NTXINDEX ) );
|
|
memset( pIndex, 0, sizeof( NTXINDEX ) );
|
|
|
|
pIndex->DiskFile = FS_ERROR;
|
|
pIndex->Owner = pArea;
|
|
return pIndex;
|
|
}
|
|
|
|
/*
|
|
* close the index file and free from memory index and tag structures
|
|
*/
|
|
static void hb_ntxIndexFree( LPNTXINDEX pIndex )
|
|
{
|
|
hb_ntxFreePageBuffer( pIndex );
|
|
if( pIndex->iTags )
|
|
{
|
|
int i;
|
|
for( i = 0; i < pIndex->iTags; i++ )
|
|
hb_ntxTagFree( pIndex->lpTags[i] );
|
|
hb_xfree( pIndex->lpTags );
|
|
}
|
|
if( pIndex->HeaderBuff )
|
|
hb_xfree( pIndex->HeaderBuff );
|
|
if( pIndex->DiskFile != FS_ERROR )
|
|
{
|
|
hb_fsClose( pIndex->DiskFile );
|
|
if( pIndex->fDelete )
|
|
{
|
|
hb_fsDelete( ( BYTE * ) ( pIndex->RealName ?
|
|
pIndex->RealName : pIndex->IndexName ) );
|
|
}
|
|
}
|
|
if( pIndex->IndexName )
|
|
hb_xfree( pIndex->IndexName );
|
|
if( pIndex->RealName )
|
|
hb_xfree( pIndex->RealName );
|
|
hb_xfree( pIndex );
|
|
}
|
|
|
|
/*
|
|
* Write tag header
|
|
*/
|
|
static ERRCODE hb_ntxIndexHeaderSave( LPNTXINDEX pIndex )
|
|
{
|
|
if( pIndex->Compound )
|
|
{
|
|
LPCTXHEADER lpCTX = ( LPCTXHEADER ) pIndex->HeaderBuff;
|
|
int iSize = pIndex->Update ? NTXBLOCKSIZE : 16;
|
|
USHORT type;
|
|
|
|
type = NTX_FLAG_COMPOUND | ( pIndex->LargeFile ? NTX_FLAG_LARGEFILE : 0 );
|
|
|
|
pIndex->Version++;
|
|
HB_PUT_LE_UINT16( lpCTX->type, type );
|
|
HB_PUT_LE_UINT16( lpCTX->ntags, pIndex->iTags );
|
|
HB_PUT_LE_UINT32( lpCTX->version, pIndex->Version );
|
|
HB_PUT_LE_UINT32( lpCTX->freepage, pIndex->NextAvail );
|
|
HB_PUT_LE_UINT32( lpCTX->filesize, pIndex->TagBlock );
|
|
|
|
if( !hb_ntxBlockWrite( pIndex, 0, ( BYTE * ) lpCTX, iSize ) )
|
|
return FAILURE;
|
|
}
|
|
pIndex->Changed = pIndex->Update = FALSE;
|
|
return SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* load new tags from index file
|
|
*/
|
|
static ERRCODE hb_ntxIndexLoad( LPNTXINDEX pIndex, char * szTagName )
|
|
{
|
|
LPTAGINFO pTag;
|
|
USHORT type;
|
|
|
|
if( !pIndex->fValidHeader )
|
|
{
|
|
if( !pIndex->HeaderBuff )
|
|
pIndex->HeaderBuff = ( BYTE * ) hb_xgrab( NTXBLOCKSIZE );
|
|
if( !hb_ntxBlockRead( pIndex, 0, pIndex->HeaderBuff, NTXBLOCKSIZE ) )
|
|
return FAILURE;
|
|
pIndex->fValidHeader = TRUE;
|
|
}
|
|
|
|
type = HB_GET_LE_UINT16( pIndex->HeaderBuff );
|
|
#if !defined( HB_NTX_NOMULTITAG )
|
|
pIndex->Compound = ( type & NTX_FLAG_COMPOUND ) != 0;
|
|
if( pIndex->Compound )
|
|
{
|
|
BYTE tagbuffer[ NTXBLOCKSIZE ];
|
|
LPCTXHEADER lpCTX = ( LPCTXHEADER ) pIndex->HeaderBuff;
|
|
LPCTXTAGITEM pTagItem = ( LPCTXTAGITEM ) lpCTX->tags;
|
|
ULONG ulBlock;
|
|
int iTags;
|
|
|
|
iTags = HB_GET_LE_UINT16( lpCTX->ntags );
|
|
if( iTags > CTX_MAX_TAGS )
|
|
return FAILURE;
|
|
pIndex->Version = HB_GET_LE_UINT32( lpCTX->version );
|
|
pIndex->NextAvail = HB_GET_LE_UINT32( lpCTX->freepage );
|
|
pIndex->TagBlock = HB_GET_LE_UINT32( lpCTX->filesize );
|
|
pIndex->LargeFile = ( type & NTX_FLAG_LARGEFILE ) != 0;
|
|
|
|
for( pIndex->iTags = 0; pIndex->iTags < iTags; pTagItem++ )
|
|
{
|
|
ulBlock = HB_GET_LE_UINT32( pTagItem->tag_header );
|
|
if( ulBlock == 0 || pTagItem->tag_name[ 0 ] <= 0x20 )
|
|
return FAILURE;
|
|
if( !hb_ntxBlockRead( pIndex, ulBlock, tagbuffer, NTXBLOCKSIZE ) )
|
|
return FAILURE;
|
|
pTag = hb_ntxTagLoad( pIndex, ulBlock, ( char * ) pTagItem->tag_name, tagbuffer );
|
|
if( !pTag )
|
|
return FAILURE;
|
|
hb_ntxTagAdd( pIndex, pTag );
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
pTag = hb_ntxTagLoad( pIndex, 0, szTagName, pIndex->HeaderBuff );
|
|
if( !pTag )
|
|
return FAILURE;
|
|
hb_ntxTagAdd( pIndex, pTag );
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* read index header and check for concurrent access
|
|
*/
|
|
static ERRCODE hb_ntxIndexHeaderRead( LPNTXINDEX pIndex )
|
|
{
|
|
USHORT type;
|
|
|
|
if( !pIndex->HeaderBuff )
|
|
pIndex->HeaderBuff = ( BYTE * ) hb_xgrab( NTXBLOCKSIZE );
|
|
|
|
if( !hb_ntxBlockRead( pIndex, 0, pIndex->HeaderBuff, NTXBLOCKSIZE ) )
|
|
return FAILURE;
|
|
|
|
type = HB_GET_LE_UINT16( pIndex->HeaderBuff );
|
|
if( ( type & NTX_FLAG_COMPOUND ) != 0 )
|
|
{
|
|
#if defined( HB_NTX_NOMULTITAG )
|
|
hb_ntxErrorRT( pIndex->Owner, EG_CORRUPTION, EDBF_CORRUPT,
|
|
pIndex->IndexName, hb_fsError(), 0 );
|
|
return FAILURE;
|
|
#else
|
|
LPCTXHEADER lpCTX = ( LPCTXHEADER ) pIndex->HeaderBuff;
|
|
ULONG ulVersion, ulNext;
|
|
/* USHORT usTags = HB_GET_LE_UINT16( lpCTX->ntags ); */
|
|
|
|
ulVersion = HB_GET_LE_UINT32( lpCTX->version );
|
|
ulNext = HB_GET_LE_UINT32( lpCTX->freepage );
|
|
pIndex->TagBlock = HB_GET_LE_UINT32( lpCTX->filesize );
|
|
|
|
if( pIndex->Version != ulVersion || pIndex->NextAvail != ulNext ||
|
|
!pIndex->Compound )
|
|
{
|
|
int i;
|
|
hb_ntxDiscardBuffers( pIndex );
|
|
pIndex->Version = ulVersion;
|
|
pIndex->NextAvail = ulNext;
|
|
pIndex->Compound = TRUE;
|
|
for( i = 1; i < pIndex->iTags; i++ )
|
|
{
|
|
pIndex->lpTags[ i ]->HeadBlock =
|
|
hb_ntxIndexTagFind( lpCTX, pIndex->lpTags[ i ]->TagName );
|
|
if( !pIndex->lpTags[ i ]->HeadBlock )
|
|
pIndex->lpTags[ i ]->RootBlock = 0;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
LPNTXHEADER lpNTX = ( LPNTXHEADER ) pIndex->HeaderBuff;
|
|
ULONG ulRootPage, ulVersion;
|
|
LPTAGINFO pTag;
|
|
|
|
if( pIndex->Compound )
|
|
{
|
|
hb_ntxErrorRT( pIndex->Owner, EG_CORRUPTION, EDBF_CORRUPT,
|
|
pIndex->IndexName, hb_fsError(), 0 );
|
|
return FAILURE;
|
|
}
|
|
pTag = pIndex->iTags ? pIndex->lpTags[0] : NULL;
|
|
|
|
ulVersion = HB_GET_LE_UINT16( lpNTX->version );
|
|
ulRootPage = HB_GET_LE_UINT32( lpNTX->root );
|
|
pIndex->NextAvail = HB_GET_LE_UINT32( lpNTX->next_page );
|
|
if( pIndex->Version != ulVersion || ( pTag &&
|
|
( pTag->Signature != type || ulRootPage != pTag->RootBlock ) ) )
|
|
{
|
|
hb_ntxDiscardBuffers( pIndex );
|
|
pIndex->Version = ulVersion;
|
|
if( pTag )
|
|
{
|
|
pTag->RootBlock = ulRootPage;
|
|
pTag->Signature = type;
|
|
hb_ntxTagUpdateFlags( pTag );
|
|
}
|
|
}
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* write modified pages to index file
|
|
*/
|
|
static void hb_ntxIndexFlush( LPNTXINDEX pIndex )
|
|
{
|
|
while( pIndex->pChanged )
|
|
{
|
|
LPPAGEINFO pPage = pIndex->pChanged;
|
|
pIndex->pChanged = pPage->pNext;
|
|
if( pPage->Changed )
|
|
{
|
|
hb_ntxPageSave( pIndex, pPage );
|
|
++pPage->iUsed;
|
|
hb_ntxPageRelease( pIndex->lpTags[0], pPage );
|
|
}
|
|
else
|
|
hb_errInternal( 9999, "hb_ntxIndexFlush: unchaged page in the list.", "", "" );
|
|
}
|
|
|
|
if( pIndex->Compound )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < pIndex->iTags; i++ )
|
|
if( pIndex->lpTags[ i ]->HdrChanged )
|
|
hb_ntxTagHeaderSave( pIndex->lpTags[ i ] );
|
|
if( pIndex->Changed )
|
|
hb_ntxIndexHeaderSave( pIndex );
|
|
}
|
|
else
|
|
{
|
|
if( pIndex->Changed || pIndex->lpTags[ 0 ]->HdrChanged )
|
|
hb_ntxTagHeaderSave( pIndex->lpTags[ 0 ] );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* lock index for reading (shared lock)
|
|
*/
|
|
static BOOL hb_ntxIndexLockRead( LPNTXINDEX pIndex )
|
|
{
|
|
BOOL fOK;
|
|
|
|
if( pIndex->lockRead > 0 || pIndex->lockWrite > 0 || !pIndex->fShared )
|
|
{
|
|
fOK = TRUE;
|
|
pIndex->lockRead++;
|
|
}
|
|
else
|
|
{
|
|
fOK = hb_dbfLockIdxFile( pIndex->DiskFile, pIndex->Owner->bLockType,
|
|
FL_LOCK | FLX_SHARED | FLX_WAIT, &pIndex->ulLockPos );
|
|
/* if fOK then check VERSION field in NTXHEADER and
|
|
* if it has been changed then discard all page buffers
|
|
*/
|
|
if( fOK )
|
|
{
|
|
pIndex->lockRead++;
|
|
if( hb_ntxIndexHeaderRead( pIndex ) != SUCCESS )
|
|
{
|
|
pIndex->lockRead--;
|
|
hb_dbfLockIdxFile( pIndex->DiskFile, pIndex->Owner->bLockType,
|
|
FL_UNLOCK, &pIndex->ulLockPos );
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
if( !fOK )
|
|
hb_ntxErrorRT( pIndex->Owner, EG_LOCK, EDBF_LOCK, pIndex->IndexName, hb_fsError(), 0 );
|
|
|
|
return fOK;
|
|
}
|
|
|
|
/*
|
|
* lock index for writing (exclusive lock)
|
|
*/
|
|
static BOOL hb_ntxIndexLockWrite( LPNTXINDEX pIndex, BOOL fCheck )
|
|
{
|
|
BOOL fOK;
|
|
|
|
if( pIndex->fReadonly )
|
|
hb_errInternal( 9101, "hb_ntxIndexLockWrite: readonly index.", "", "" );
|
|
|
|
if( pIndex->lockRead )
|
|
hb_errInternal( 9105, "hb_ntxIndexLockWrite: writeLock after readLock.", "", "" );
|
|
|
|
if( pIndex->lockWrite > 0 || !pIndex->fShared )
|
|
{
|
|
fOK = TRUE;
|
|
pIndex->lockWrite++;
|
|
}
|
|
else
|
|
{
|
|
fOK = hb_dbfLockIdxFile( pIndex->DiskFile, pIndex->Owner->bLockType,
|
|
FL_LOCK | FLX_WAIT, &pIndex->ulLockPos );
|
|
/* if fOK then check VERSION field in NTXHEADER and
|
|
* if it has been changed then discard all page buffers
|
|
*/
|
|
if( fOK )
|
|
{
|
|
pIndex->lockWrite++;
|
|
if( fCheck && hb_ntxIndexHeaderRead( pIndex ) != SUCCESS )
|
|
{
|
|
pIndex->lockWrite--;
|
|
hb_dbfLockIdxFile( pIndex->DiskFile, pIndex->Owner->bLockType,
|
|
FL_UNLOCK, &pIndex->ulLockPos );
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
if( !fOK )
|
|
hb_ntxErrorRT( pIndex->Owner, EG_LOCK, EDBF_LOCK, pIndex->IndexName, hb_fsError(), 0 );
|
|
|
|
return fOK;
|
|
}
|
|
|
|
/*
|
|
* remove index read lock (shared lock)
|
|
*/
|
|
static BOOL hb_ntxIndexUnLockRead( LPNTXINDEX pIndex )
|
|
{
|
|
BOOL fOK;
|
|
|
|
#ifdef HB_NTX_DEBUG
|
|
int i;
|
|
for( i = 0; i < pIndex->iTags; i++ )
|
|
hb_ntxTagCheckBuffers( pIndex->lpTags[ i ] );
|
|
#endif
|
|
|
|
pIndex->lockRead--;
|
|
if( pIndex->lockRead < 0 )
|
|
hb_errInternal( 9106, "hb_ntxIndexUnLockRead: bad count of locks.", "", "" );
|
|
|
|
if( pIndex->lockRead || pIndex->lockWrite || !pIndex->fShared )
|
|
{
|
|
fOK = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pIndex->fValidHeader = FALSE;
|
|
fOK = hb_dbfLockIdxFile( pIndex->DiskFile, pIndex->Owner->bLockType,
|
|
FL_UNLOCK, &pIndex->ulLockPos );
|
|
}
|
|
if( !fOK )
|
|
hb_errInternal( 9108, "hb_ntxIndexUnLockRead: unlock error.", "", "" );
|
|
|
|
return fOK;
|
|
}
|
|
|
|
/*
|
|
* remove index write lock (exclusive lock)
|
|
*/
|
|
static BOOL hb_ntxIndexUnLockWrite( LPNTXINDEX pIndex )
|
|
{
|
|
BOOL fOK;
|
|
|
|
#ifdef HB_NTX_DEBUG
|
|
int i;
|
|
for( i = 0; i < pIndex->iTags; i++ )
|
|
hb_ntxTagCheckBuffers( pIndex->lpTags[ i ] );
|
|
#endif
|
|
|
|
if( pIndex->lockWrite <= 0 )
|
|
hb_errInternal( 9106, "hb_ntxIndexUnLockWrite: bad count of locks.", "", "" );
|
|
if( pIndex->lockRead )
|
|
hb_errInternal( 9105, "hb_ntxIndexUnLockWrite: writeUnLock before readUnLock.", "", "" );
|
|
|
|
hb_ntxIndexFlush( pIndex );
|
|
pIndex->lockWrite--;
|
|
|
|
if( pIndex->lockWrite || !pIndex->fShared )
|
|
{
|
|
fOK = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pIndex->fValidHeader = FALSE;
|
|
fOK = hb_dbfLockIdxFile( pIndex->DiskFile, pIndex->Owner->bLockType,
|
|
FL_UNLOCK, &pIndex->ulLockPos );
|
|
}
|
|
if( !fOK )
|
|
hb_errInternal( 9108, "hb_ntxIndexUnLockWrite: unlock error.", "", "" );
|
|
|
|
return fOK;
|
|
}
|
|
|
|
/*
|
|
* lock tag for reading (shared lock)
|
|
*/
|
|
static BOOL hb_ntxTagLockRead( LPTAGINFO pTag )
|
|
{
|
|
BOOL fOK = FALSE;
|
|
|
|
if( hb_ntxIndexLockRead( pTag->Owner ) )
|
|
{
|
|
fOK = hb_ntxTagHeaderCheck( pTag );
|
|
if( !fOK )
|
|
{
|
|
hb_ntxIndexUnLockRead( pTag->Owner );
|
|
hb_ntxErrorRT( pTag->Owner->Owner, EG_CORRUPTION, EDBF_CORRUPT,
|
|
pTag->Owner->IndexName, 0, 0 );
|
|
}
|
|
}
|
|
return fOK;
|
|
}
|
|
|
|
/*
|
|
* lock tag for writing (exclusive lock)
|
|
*/
|
|
static BOOL hb_ntxTagLockWrite( LPTAGINFO pTag )
|
|
{
|
|
BOOL fOK = FALSE;
|
|
|
|
if( hb_ntxIndexLockWrite( pTag->Owner, TRUE ) )
|
|
{
|
|
fOK = hb_ntxTagHeaderCheck( pTag );
|
|
if( !fOK )
|
|
{
|
|
hb_ntxIndexUnLockWrite( pTag->Owner );
|
|
hb_ntxErrorRT( pTag->Owner->Owner, EG_CORRUPTION, EDBF_CORRUPT,
|
|
pTag->Owner->IndexName, 0, 0 );
|
|
}
|
|
}
|
|
return fOK;
|
|
}
|
|
|
|
/*
|
|
* remove tag read lock (shared lock)
|
|
*/
|
|
static BOOL hb_ntxTagUnLockRead( LPTAGINFO pTag )
|
|
{
|
|
return hb_ntxIndexUnLockRead( pTag->Owner );
|
|
}
|
|
|
|
/*
|
|
* remove tag write lock (exclusive lock)
|
|
*/
|
|
static BOOL hb_ntxTagUnLockWrite( LPTAGINFO pTag )
|
|
{
|
|
return hb_ntxIndexUnLockWrite( pTag->Owner );
|
|
}
|
|
|
|
/*
|
|
* retrive key from page
|
|
*/
|
|
static void hb_ntxPageGetKey( LPPAGEINFO pPage, USHORT uiKey, LPKEYINFO pKey, USHORT uiLen )
|
|
{
|
|
if( uiKey < pPage->uiKeys )
|
|
{
|
|
memcpy( pKey->key, hb_ntxGetKeyVal( pPage, uiKey ), uiLen );
|
|
pKey->Xtra = hb_ntxGetKeyRec( pPage, uiKey );
|
|
pKey->Tag = pPage->Page;
|
|
}
|
|
else
|
|
{
|
|
pKey->Xtra = pKey->Tag = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* set next page and key in page path
|
|
*/
|
|
static void hb_ntxTagSetPageStack( LPTAGINFO pTag, ULONG ulPage, USHORT uiKey )
|
|
{
|
|
if( pTag->stackLevel == pTag->stackSize )
|
|
{
|
|
if( pTag->stackSize == 0 )
|
|
{
|
|
pTag->stackSize = NTX_STACKSIZE;
|
|
pTag->stack = (LPTREESTACK) hb_xgrab( sizeof(TREE_STACK) * NTX_STACKSIZE );
|
|
}
|
|
else
|
|
{
|
|
pTag->stackSize += NTX_STACKSIZE;
|
|
pTag->stack = ( LPTREESTACK ) hb_xrealloc( pTag->stack,
|
|
sizeof( TREE_STACK ) * pTag->stackSize );
|
|
}
|
|
}
|
|
pTag->stack[ pTag->stackLevel ].page = ulPage;
|
|
pTag->stack[ pTag->stackLevel++ ].ikey = uiKey;
|
|
}
|
|
|
|
/*
|
|
* go down from the given index page to the first key
|
|
*/
|
|
static LPPAGEINFO hb_ntxPageTopMove( LPTAGINFO pTag, ULONG ulPage )
|
|
{
|
|
LPPAGEINFO pPage = NULL;
|
|
|
|
do
|
|
{
|
|
if( pPage )
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
pPage = hb_ntxPageLoad( pTag, ulPage );
|
|
if( ! pPage )
|
|
return NULL;
|
|
#ifdef HB_NTX_DEBUG_EXT
|
|
if( pPage->uiKeys == 0 && pTag->stackLevel > 0 )
|
|
{
|
|
hb_errInternal( 9201, "hb_ntxPageTopMove: index corrupted.", "", "" );
|
|
return NULL;
|
|
}
|
|
#endif
|
|
ulPage = hb_ntxGetKeyPage( pPage, 0 );
|
|
hb_ntxTagSetPageStack( pTag, pPage->Page, 0 );
|
|
}
|
|
while( ulPage );
|
|
|
|
return pPage;
|
|
}
|
|
|
|
/*
|
|
* go down from the given index page to the last key
|
|
*/
|
|
static LPPAGEINFO hb_ntxPageBottomMove( LPTAGINFO pTag, ULONG ulPage )
|
|
{
|
|
LPPAGEINFO pPage = NULL;
|
|
|
|
do
|
|
{
|
|
if( pPage )
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
pPage = hb_ntxPageLoad( pTag, ulPage );
|
|
if( ! pPage )
|
|
return NULL;
|
|
#ifdef HB_NTX_DEBUG_EXT
|
|
if( pPage->uiKeys == 0 && pTag->stackLevel > 0 )
|
|
{
|
|
hb_errInternal( 9201, "hb_ntxPageBottomMove: index corrupted.", "", "" );
|
|
return NULL;
|
|
}
|
|
#endif
|
|
ulPage = hb_ntxGetKeyPage( pPage, pPage->uiKeys );
|
|
hb_ntxTagSetPageStack( pTag, pPage->Page, pPage->uiKeys -
|
|
( ulPage || pPage->uiKeys == 0 ? 0 : 1 ) );
|
|
}
|
|
while( ulPage );
|
|
|
|
return pPage;
|
|
}
|
|
|
|
/*
|
|
* set page path to the first key in tag
|
|
*/
|
|
static BOOL hb_ntxTagTopKey( LPTAGINFO pTag )
|
|
{
|
|
LPPAGEINFO pPage;
|
|
int iKeys;
|
|
|
|
pTag->stackLevel = 0;
|
|
pPage = hb_ntxPageTopMove( pTag, 0 );
|
|
if( ! pPage )
|
|
return FALSE;
|
|
hb_ntxPageGetKey( pPage, 0, pTag->CurKeyInfo, pTag->KeyLength );
|
|
iKeys = pPage->uiKeys;
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
return iKeys != 0;
|
|
}
|
|
|
|
/*
|
|
* set page path to the last key in tag
|
|
*/
|
|
static BOOL hb_ntxTagBottomKey( LPTAGINFO pTag )
|
|
{
|
|
LPPAGEINFO pPage;
|
|
int iKeys;
|
|
|
|
pTag->stackLevel = 0;
|
|
pPage = hb_ntxPageBottomMove( pTag, 0 );
|
|
if( ! pPage )
|
|
return FALSE;
|
|
hb_ntxPageGetKey( pPage, pTag->stack[ pTag->stackLevel - 1 ].ikey,
|
|
pTag->CurKeyInfo, pTag->KeyLength );
|
|
iKeys = pPage->uiKeys;
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
return iKeys != 0;
|
|
}
|
|
|
|
/*
|
|
* update page path to the next key in tag
|
|
*/
|
|
static BOOL hb_ntxTagNextKey( LPTAGINFO pTag )
|
|
{
|
|
int iLevel = pTag->stackLevel - 1;
|
|
LPPAGEINFO pPage;
|
|
ULONG ulPage = 0;
|
|
|
|
if( iLevel >= 0 )
|
|
{
|
|
pPage = hb_ntxPageLoad( pTag, pTag->stack[ iLevel ].page );
|
|
if( ! pPage )
|
|
return FALSE;
|
|
if( pTag->stack[ iLevel ].ikey < pPage->uiKeys )
|
|
ulPage = hb_ntxGetKeyPage( pPage, pTag->stack[ iLevel ].ikey + 1 );
|
|
if( ulPage || pTag->stack[ iLevel ].ikey + 1 < pPage->uiKeys )
|
|
{
|
|
pTag->stack[ iLevel ].ikey++;
|
|
if( ulPage )
|
|
{
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
pPage = hb_ntxPageTopMove( pTag, ulPage );
|
|
if( ! pPage )
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while( --iLevel >= 0 )
|
|
{
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
pPage = hb_ntxPageLoad( pTag, pTag->stack[ iLevel ].page );
|
|
if( ! pPage )
|
|
return FALSE;
|
|
if( pTag->stack[ iLevel ].ikey < pPage->uiKeys )
|
|
break;
|
|
}
|
|
if( iLevel < 0 )
|
|
{
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
return FALSE;
|
|
}
|
|
pTag->stackLevel = iLevel + 1;
|
|
}
|
|
hb_ntxPageGetKey( pPage, pTag->stack[ pTag->stackLevel - 1 ].ikey,
|
|
pTag->CurKeyInfo, pTag->KeyLength );
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* update page path to the previous key in tag
|
|
*/
|
|
static BOOL hb_ntxTagPrevKey( LPTAGINFO pTag )
|
|
{
|
|
int iLevel = pTag->stackLevel - 1;
|
|
LPPAGEINFO pPage;
|
|
ULONG ulPage;
|
|
|
|
if( iLevel >= 0 )
|
|
{
|
|
pPage = hb_ntxPageLoad( pTag, pTag->stack[ iLevel ].page );
|
|
if( ! pPage )
|
|
return FALSE;
|
|
ulPage = hb_ntxGetKeyPage( pPage, pTag->stack[ iLevel ].ikey );
|
|
if( ulPage )
|
|
{
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
pPage = hb_ntxPageBottomMove( pTag, ulPage );
|
|
if( ! pPage )
|
|
return FALSE;
|
|
}
|
|
else if( pTag->stack[ iLevel ].ikey )
|
|
{
|
|
pTag->stack[ iLevel ].ikey--;
|
|
}
|
|
else
|
|
{
|
|
while( --iLevel >= 0 )
|
|
{
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
pPage = hb_ntxPageLoad( pTag, pTag->stack[ iLevel ].page );
|
|
if( ! pPage )
|
|
return FALSE;
|
|
if( pTag->stack[ iLevel ].ikey )
|
|
{
|
|
pTag->stack[ iLevel ].ikey--;
|
|
break;
|
|
}
|
|
}
|
|
if( iLevel < 0 )
|
|
{
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
return FALSE;
|
|
}
|
|
pTag->stackLevel = iLevel + 1;
|
|
}
|
|
hb_ntxPageGetKey( pPage, pTag->stack[ pTag->stackLevel - 1 ].ikey,
|
|
pTag->CurKeyInfo, pTag->KeyLength );
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* find a key value in page
|
|
*/
|
|
static int hb_ntxPageKeyFind( LPTAGINFO pTag, LPPAGEINFO pPage,
|
|
char* key, SHORT keylen, BOOL fNext,
|
|
ULONG ulRecNo, BOOL *fStop )
|
|
{
|
|
SHORT iLast = -1, iBegin = 0, iEnd = pPage->uiKeys - 1, k, i;
|
|
|
|
*fStop = FALSE;
|
|
while( iBegin <= iEnd )
|
|
{
|
|
i = ( iBegin + iEnd ) >> 1;
|
|
k = hb_ntxValCompare( pTag, key, keylen, hb_ntxGetKeyVal( pPage, i ),
|
|
pTag->KeyLength, FALSE );
|
|
if( k == 0 )
|
|
{
|
|
if( ulRecNo != 0 && pTag->fSortRec )
|
|
{
|
|
ULONG ulRec = hb_ntxGetKeyRec( pPage, i );
|
|
if( ulRecNo < ulRec )
|
|
k = -1;
|
|
else if( ulRecNo > ulRec )
|
|
k = 1;
|
|
else
|
|
{
|
|
*fStop = TRUE;
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
else if( !pTag->AscendKey )
|
|
k = -k;
|
|
if( fNext ? k >= 0 : k > 0 )
|
|
iBegin = i + 1;
|
|
else
|
|
{
|
|
if( k == 0 && !ulRecNo )
|
|
*fStop = TRUE;
|
|
iLast = i;
|
|
iEnd = i - 1;
|
|
}
|
|
}
|
|
return iLast >= 0 ? iLast : pPage->uiKeys;
|
|
}
|
|
|
|
/*
|
|
* find a record in page starting from given key
|
|
*/
|
|
static BOOL hb_ntxPageFindRecNo( LPPAGEINFO pPage, int * iStart, ULONG ulRecno )
|
|
{
|
|
int iKey = *iStart;
|
|
while( iKey < pPage->uiKeys )
|
|
{
|
|
if( hb_ntxGetKeyRec( pPage, iKey ) == ulRecno )
|
|
{
|
|
*iStart = iKey;
|
|
return TRUE;
|
|
}
|
|
iKey++;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* set page path to given key in tag
|
|
*/
|
|
static BOOL hb_ntxTagKeyFind( LPTAGINFO pTag, LPKEYINFO pKey, USHORT uiLen )
|
|
{
|
|
LPPAGEINFO pPage = NULL;
|
|
ULONG ulPage = 0, ulRecNo = 0;
|
|
int iKey;
|
|
BOOL fStop = FALSE, fNext = FALSE, fPrev = FALSE, fOut = FALSE;
|
|
|
|
if( pKey->Tag == NTX_MAX_REC_NUM ) /* for key add */
|
|
{
|
|
if( pTag->fSortRec )
|
|
ulRecNo = pKey->Xtra;
|
|
else
|
|
fNext = TRUE;
|
|
}
|
|
else if( pKey->Xtra == NTX_MAX_REC_NUM ) /* for seek last */
|
|
fNext = fPrev = TRUE;
|
|
else if( pKey->Xtra != NTX_IGNORE_REC_NUM ) /* for key del and current key */
|
|
ulRecNo = pKey->Xtra;
|
|
/* else -> normal seek */
|
|
|
|
pTag->stackLevel = 0;
|
|
do
|
|
{
|
|
if( pPage )
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
pPage = hb_ntxPageLoad( pTag, ulPage );
|
|
if( ! pPage )
|
|
return FALSE;
|
|
iKey = hb_ntxPageKeyFind( pTag, pPage, pKey->key, uiLen, fNext, ulRecNo, &fStop );
|
|
hb_ntxTagSetPageStack( pTag, pPage->Page, iKey );
|
|
if( fStop && ulRecNo && pTag->fSortRec )
|
|
break;
|
|
ulPage = hb_ntxGetKeyPage( pPage, iKey );
|
|
} while( ulPage != 0 );
|
|
|
|
if( ulRecNo && !pTag->fSortRec ) /* small hack - should speedup in some cases */
|
|
{
|
|
if( hb_ntxPageFindRecNo( pPage, &iKey, ulRecNo ) )
|
|
pTag->stack[ pTag->stackLevel - 1 ].ikey = iKey;
|
|
}
|
|
|
|
hb_ntxPageGetKey( pPage, iKey, pTag->CurKeyInfo, pTag->KeyLength );
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
|
|
if( ulRecNo )
|
|
{
|
|
if( !pTag->fSortRec )
|
|
{
|
|
fStop = TRUE;
|
|
while( fStop && ulRecNo != pTag->CurKeyInfo->Xtra )
|
|
{
|
|
if( ! hb_ntxTagNextKey( pTag ) ) /* Tag EOF */
|
|
{
|
|
fOut = TRUE;
|
|
fStop = FALSE;
|
|
}
|
|
else
|
|
{
|
|
fStop = hb_ntxValCompare( pTag, pKey->key, uiLen,
|
|
pTag->CurKeyInfo->key, pTag->KeyLength,
|
|
FALSE ) == 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if( fPrev )
|
|
{
|
|
if( !hb_ntxTagPrevKey( pTag ) )
|
|
{
|
|
fOut = TRUE;
|
|
fStop = FALSE;
|
|
}
|
|
else
|
|
{
|
|
fStop = hb_ntxValCompare( pTag, pKey->key, uiLen, pTag->CurKeyInfo->key,
|
|
pTag->KeyLength, FALSE ) == 0;
|
|
}
|
|
}
|
|
else if( !fNext && !fStop && pTag->CurKeyInfo->Xtra == 0 )
|
|
{
|
|
if( ! hb_ntxTagNextKey( pTag ) ) /* Tag EOF */
|
|
{
|
|
fOut = TRUE;
|
|
fStop = FALSE;
|
|
}
|
|
else
|
|
{
|
|
fStop = hb_ntxValCompare( pTag, pKey->key, uiLen,
|
|
pTag->CurKeyInfo->key, pTag->KeyLength,
|
|
FALSE ) == 0;
|
|
}
|
|
}
|
|
|
|
pTag->TagBOF = pTag->TagEOF = fOut || pTag->CurKeyInfo->Xtra == 0;
|
|
|
|
return fStop;
|
|
}
|
|
|
|
/*
|
|
* set key in the given tag page
|
|
*/
|
|
static void hb_ntxPageKeySet( LPTAGINFO pTag, LPPAGEINFO pPage, USHORT uiPos,
|
|
ULONG ulPage, ULONG ulRec, char * keyVal )
|
|
{
|
|
hb_ntxSetKeyPage( pPage, uiPos, ulPage );
|
|
hb_ntxSetKeyRec( pPage, uiPos, ulRec );
|
|
memcpy( hb_ntxGetKeyVal( pPage, uiPos ), keyVal, pTag->KeyLength );
|
|
pPage->Changed = TRUE;
|
|
}
|
|
|
|
/*
|
|
* add key to tag page
|
|
*/
|
|
static void hb_ntxPageKeyAdd( LPTAGINFO pTag, LPPAGEINFO pPage, USHORT uiPos,
|
|
ULONG ulPage, ULONG ulRec, char * keyVal )
|
|
{
|
|
USHORT u, ntmp = hb_ntxGetKeyOffset( pPage, pPage->uiKeys + 1 );
|
|
|
|
/* TODO?: update to keep last key pointer fixed */
|
|
for( u = pPage->uiKeys + 1; u > uiPos; u-- )
|
|
{
|
|
hb_ntxSetKeyOffset( pPage, u, hb_ntxGetKeyOffset( pPage, u - 1 ) );
|
|
}
|
|
hb_ntxSetKeyOffset( pPage, uiPos, ntmp );
|
|
pPage->uiKeys++;
|
|
|
|
hb_ntxPageKeySet( pTag, pPage, uiPos, ulPage, ulRec, keyVal );
|
|
#ifdef HB_NTX_DEBUG
|
|
hb_ntxPageCheckKeys( pPage, pTag, uiPos, 41 );
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* del key from the page
|
|
*/
|
|
static void hb_ntxPageKeyDel( LPPAGEINFO pPage, USHORT uiPos )
|
|
{
|
|
USHORT u, ntmp = hb_ntxGetKeyOffset( pPage, uiPos );
|
|
|
|
/* TODO?: update to keep last key pointer fixed */
|
|
for( u = uiPos; u < pPage->uiKeys; u++ )
|
|
hb_ntxSetKeyOffset( pPage, u, hb_ntxGetKeyOffset( pPage, u + 1 ) );
|
|
hb_ntxSetKeyOffset( pPage, pPage->uiKeys, ntmp );
|
|
|
|
pPage->uiKeys--;
|
|
pPage->Changed = TRUE;
|
|
}
|
|
|
|
/*
|
|
* split single page into two and return key to the new one
|
|
*/
|
|
static LPKEYINFO hb_ntxPageSplit( LPTAGINFO pTag, LPPAGEINFO pPage,
|
|
LPKEYINFO pKey, USHORT uiPos )
|
|
{
|
|
LPPAGEINFO pNewPage = hb_ntxPageNew( pTag, FALSE );
|
|
LPKEYINFO pKeyNew;
|
|
USHORT uiKeys = pPage->uiKeys + 1, uiLen = pTag->KeyLength + 8,
|
|
i, j, u, uiHalf;
|
|
ULONG ulPage;
|
|
|
|
if( ! pNewPage )
|
|
return NULL;
|
|
pKeyNew = hb_ntxKeyNew( NULL, pTag->KeyLength );
|
|
|
|
uiHalf = uiKeys >> 1;
|
|
|
|
j = 0;
|
|
while( pNewPage->uiKeys < uiHalf )
|
|
{
|
|
if( pNewPage->uiKeys == uiPos )
|
|
{
|
|
hb_ntxSetKeyPage( pNewPage, pNewPage->uiKeys, pKey->Tag );
|
|
hb_ntxSetKeyRec( pNewPage, pNewPage->uiKeys, pKey->Xtra );
|
|
memcpy( hb_ntxGetKeyVal( pNewPage, pNewPage->uiKeys ), pKey->key, pTag->KeyLength );
|
|
}
|
|
else
|
|
{
|
|
memcpy( hb_ntxGetKeyPtr( pNewPage, pNewPage->uiKeys ),
|
|
hb_ntxGetKeyPtr( pPage, j ), uiLen );
|
|
j++;
|
|
}
|
|
pNewPage->uiKeys++;
|
|
}
|
|
|
|
if( uiHalf == uiPos )
|
|
{
|
|
pKeyNew->Xtra = pKey->Xtra;
|
|
memcpy( pKeyNew->key, pKey->key, pTag->KeyLength );
|
|
hb_ntxSetKeyPage( pNewPage, pNewPage->uiKeys, pKey->Tag );
|
|
}
|
|
else
|
|
{
|
|
pKeyNew->Xtra = hb_ntxGetKeyRec( pPage, j );
|
|
memcpy( pKeyNew->key, hb_ntxGetKeyVal( pPage, j ), pTag->KeyLength );
|
|
hb_ntxSetKeyPage( pNewPage, pNewPage->uiKeys, hb_ntxGetKeyPage( pPage, j ) );
|
|
j++;
|
|
}
|
|
pKeyNew->Tag = pNewPage->Page;
|
|
|
|
i = 0;
|
|
while( ++uiHalf < uiKeys )
|
|
{
|
|
if( uiHalf == uiPos )
|
|
{
|
|
hb_ntxSetKeyPage( pPage, i, pKey->Tag );
|
|
hb_ntxSetKeyRec( pPage, i, pKey->Xtra );
|
|
memcpy( hb_ntxGetKeyVal( pPage, i ), pKey->key, pTag->KeyLength );
|
|
}
|
|
else
|
|
{
|
|
u = hb_ntxGetKeyOffset( pPage, j );
|
|
hb_ntxSetKeyOffset( pPage, j, hb_ntxGetKeyOffset( pPage, i ) );
|
|
hb_ntxSetKeyOffset( pPage, i, u );
|
|
j++;
|
|
}
|
|
i++;
|
|
}
|
|
ulPage = hb_ntxGetKeyPage( pPage, pPage->uiKeys );
|
|
hb_ntxSetKeyPage( pPage, pPage->uiKeys, 0 );
|
|
hb_ntxSetKeyPage( pPage, i, ulPage );
|
|
pPage->uiKeys = i;
|
|
|
|
pPage->Changed = pNewPage->Changed = TRUE;
|
|
#ifdef HB_NTX_DEBUG
|
|
hb_ntxPageCheckKeys( pNewPage, pTag, uiPos, 1 );
|
|
hb_ntxPageCheckKeys( pPage, pTag, uiPos - pNewPage->uiKeys, 2 );
|
|
#endif
|
|
hb_ntxPageRelease( pTag, pNewPage );
|
|
|
|
return pKeyNew;
|
|
}
|
|
|
|
/*
|
|
* join two neighbour pages and update the parent page key
|
|
*/
|
|
static void hb_ntxPageJoin( LPTAGINFO pTag, LPPAGEINFO pBasePage, USHORT uiPos,
|
|
LPPAGEINFO pFirst, LPPAGEINFO pLast )
|
|
{
|
|
USHORT uiLen = pTag->KeyLength + 8, i;
|
|
|
|
hb_ntxSetKeyRec( pFirst, pFirst->uiKeys, hb_ntxGetKeyRec( pBasePage, uiPos ) );
|
|
memcpy( hb_ntxGetKeyVal( pFirst, pFirst->uiKeys ),
|
|
hb_ntxGetKeyVal( pBasePage, uiPos ), pTag->KeyLength );
|
|
pFirst->uiKeys++;
|
|
hb_ntxPageKeyDel( pBasePage, uiPos );
|
|
hb_ntxSetKeyPage( pBasePage, uiPos, pFirst->Page );
|
|
for( i = 0; i < pLast->uiKeys; i++ )
|
|
{
|
|
memcpy( hb_ntxGetKeyPtr( pFirst, pFirst->uiKeys ),
|
|
hb_ntxGetKeyPtr( pLast, i ), uiLen );
|
|
pFirst->uiKeys++;
|
|
}
|
|
hb_ntxSetKeyPage( pFirst, pFirst->uiKeys, hb_ntxGetKeyPage( pLast, pLast->uiKeys ) );
|
|
pLast->uiKeys = 0;
|
|
hb_ntxPageFree( pTag, pLast );
|
|
pFirst->Changed = pLast->Changed = TRUE;
|
|
#ifdef HB_NTX_DEBUG
|
|
hb_ntxPageCheckKeys( pBasePage, pTag, uiPos, 11 );
|
|
hb_ntxPageCheckKeys( pFirst, pTag, 0, 12 );
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* balance keys in two neighbour pages and update the parent page key
|
|
*/
|
|
static void hb_ntxBalancePages( LPTAGINFO pTag, LPPAGEINFO pBasePage, USHORT uiPos,
|
|
LPPAGEINFO pFirst, LPPAGEINFO pLast )
|
|
{
|
|
USHORT uiLen = pTag->KeyLength + 8, n;
|
|
int i, j, iMove = ( ( pFirst->uiKeys + pLast->uiKeys + 1 ) >> 1 ) - pFirst->uiKeys;
|
|
|
|
/*
|
|
* such situation should not exist even max keys, though it does not cost
|
|
* much and I want to be able to call hb_ntxBalancePages in any case for
|
|
* some advanced balancing
|
|
*/
|
|
if( iMove == 0 )
|
|
return;
|
|
|
|
#ifdef HB_NTX_DEBUG
|
|
hb_ntxPageCheckKeys( pBasePage, pTag, uiPos, 31 );
|
|
hb_ntxPageCheckKeys( pFirst, pTag, iMove, 32 );
|
|
hb_ntxPageCheckKeys( pLast, pTag, iMove, 33 );
|
|
#endif
|
|
|
|
if( iMove > 0 )
|
|
{
|
|
hb_ntxSetKeyRec( pFirst, pFirst->uiKeys, hb_ntxGetKeyRec( pBasePage, uiPos ) );
|
|
memcpy( hb_ntxGetKeyVal( pFirst, pFirst->uiKeys ),
|
|
hb_ntxGetKeyVal( pBasePage, uiPos ), pTag->KeyLength );
|
|
pFirst->uiKeys++;
|
|
i = 0;
|
|
while( --iMove )
|
|
{
|
|
memcpy( hb_ntxGetKeyPtr( pFirst, pFirst->uiKeys ),
|
|
hb_ntxGetKeyPtr( pLast, i ), uiLen );
|
|
pFirst->uiKeys++;
|
|
i++;
|
|
}
|
|
hb_ntxSetKeyRec( pBasePage, uiPos, hb_ntxGetKeyRec( pLast, i ) );
|
|
memcpy( hb_ntxGetKeyVal( pBasePage, uiPos ),
|
|
hb_ntxGetKeyVal( pLast, i ), pTag->KeyLength );
|
|
hb_ntxSetKeyPage( pFirst, pFirst->uiKeys, hb_ntxGetKeyPage( pLast, i ) );
|
|
i++;
|
|
pLast->uiKeys -= i;
|
|
/* TODO?: update to keep last key pointer fixed */
|
|
for( j = 0; j <= pLast->uiKeys; j++ )
|
|
{
|
|
n = hb_ntxGetKeyOffset( pLast, j );
|
|
hb_ntxSetKeyOffset( pLast, j, hb_ntxGetKeyOffset( pLast, j + i ) );
|
|
hb_ntxSetKeyOffset( pLast, j + i, n );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* TODO?: update to keep last key pointer fixed */
|
|
for( j = pLast->uiKeys; j >= 0; j-- )
|
|
{
|
|
n = hb_ntxGetKeyOffset( pLast, j - iMove );
|
|
hb_ntxSetKeyOffset( pLast, j - iMove, hb_ntxGetKeyOffset( pLast, j ) );
|
|
hb_ntxSetKeyOffset( pLast, j, n );
|
|
}
|
|
i = -iMove - 1;
|
|
hb_ntxSetKeyRec( pLast, i, hb_ntxGetKeyRec( pBasePage, uiPos ) );
|
|
memcpy( hb_ntxGetKeyVal( pLast, i ),
|
|
hb_ntxGetKeyVal( pBasePage, uiPos ), pTag->KeyLength );
|
|
hb_ntxSetKeyPage( pLast, i, hb_ntxGetKeyPage( pFirst, pFirst->uiKeys ) );
|
|
while( --i >= 0 )
|
|
{
|
|
pFirst->uiKeys--;
|
|
memcpy( hb_ntxGetKeyPtr( pLast, i ),
|
|
hb_ntxGetKeyPtr( pFirst, pFirst->uiKeys ), uiLen );
|
|
}
|
|
pLast->uiKeys -= iMove;
|
|
pFirst->uiKeys--;
|
|
hb_ntxSetKeyRec( pBasePage, uiPos, hb_ntxGetKeyRec( pFirst, pFirst->uiKeys ) );
|
|
memcpy( hb_ntxGetKeyVal( pBasePage, uiPos ),
|
|
hb_ntxGetKeyVal( pFirst, pFirst->uiKeys ), pTag->KeyLength );
|
|
}
|
|
pFirst->Changed = pLast->Changed = pBasePage->Changed = TRUE;
|
|
#ifdef HB_NTX_DEBUG
|
|
hb_ntxPageCheckKeys( pBasePage, pTag, uiPos, 21 );
|
|
hb_ntxPageCheckKeys( pFirst, pTag, iMove, 22 );
|
|
hb_ntxPageCheckKeys( pLast, pTag, iMove, 23 );
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* add key to the index at the curret page path
|
|
*/
|
|
static BOOL hb_ntxTagKeyAdd( LPTAGINFO pTag, LPKEYINFO pKey )
|
|
{
|
|
int iLevel, iKey;
|
|
LPPAGEINFO pPage = NULL;
|
|
LPKEYINFO pNewKey = NULL;
|
|
ULONG ulPage;
|
|
BOOL fFound, fBottom = FALSE;
|
|
|
|
if( pTag->UniqueKey )
|
|
{
|
|
ULONG ulRecNo = pKey->Xtra;
|
|
|
|
pKey->Xtra = NTX_IGNORE_REC_NUM;
|
|
fFound = hb_ntxTagKeyFind( pTag, pKey, pTag->KeyLength );
|
|
pKey->Xtra = ulRecNo;
|
|
if( fFound )
|
|
return FALSE;
|
|
fBottom = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pKey->Tag = NTX_MAX_REC_NUM;
|
|
fFound = hb_ntxTagKeyFind( pTag, pKey, pTag->KeyLength );
|
|
pKey->Tag = 0;
|
|
if( fFound )
|
|
{
|
|
if( pTag->MultiKey )
|
|
fBottom = TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
}
|
|
pTag->CurKeyInfo = hb_ntxKeyCopy( pTag->CurKeyInfo, pKey, pTag->KeyLength );
|
|
|
|
iLevel = pTag->stackLevel - 1;
|
|
if( fBottom )
|
|
{
|
|
pPage = hb_ntxPageLoad( pTag, pTag->stack[ iLevel ].page );
|
|
if( ! pPage )
|
|
return FALSE;
|
|
ulPage = hb_ntxGetKeyPage( pPage, pTag->stack[ iLevel ].ikey );
|
|
if( ulPage )
|
|
{
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
pPage = hb_ntxPageBottomMove( pTag, ulPage );
|
|
if( ! pPage )
|
|
return FALSE;
|
|
iLevel = pTag->stackLevel - 1;
|
|
if( pTag->stack[ iLevel ].ikey < pPage->uiKeys )
|
|
pTag->stack[ iLevel ].ikey++;
|
|
}
|
|
}
|
|
|
|
while( iLevel >= 0 && pKey )
|
|
{
|
|
if( pPage )
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
pPage = hb_ntxPageLoad( pTag, pTag->stack[ iLevel ].page );
|
|
if( ! pPage )
|
|
{
|
|
if( pNewKey )
|
|
hb_ntxKeyFree( pNewKey );
|
|
pTag->stackLevel = 0;
|
|
return FALSE;
|
|
}
|
|
iKey = pTag->stack[ iLevel ].ikey;
|
|
if( pPage->uiKeys < pTag->MaxKeys )
|
|
{
|
|
hb_ntxPageKeyAdd( pTag, pPage, iKey, pKey->Tag, pKey->Xtra, pKey->key );
|
|
pKey = NULL;
|
|
}
|
|
else
|
|
{
|
|
pTag->stackLevel = 0;
|
|
#if defined( HB_NTX_STRONG_BALANCE )
|
|
if( iLevel > 0 )
|
|
{
|
|
LPPAGEINFO pBasePage;
|
|
USHORT uiFirst, uiLast, uiBaseKey;
|
|
pBasePage = hb_ntxPageLoad( pTag, pTag->stack[ iLevel - 1 ].page );
|
|
if( !pBasePage )
|
|
{
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
if( pNewKey )
|
|
hb_ntxKeyFree( pNewKey );
|
|
return FALSE;
|
|
}
|
|
uiFirst = uiLast = uiBaseKey = pTag->stack[ iLevel -1 ].ikey;
|
|
if( uiLast < pBasePage->uiKeys && hb_ntxGetKeyPage( pBasePage, uiLast + 1 ) != 0 )
|
|
uiLast++;
|
|
else if( uiFirst > 0 && hb_ntxGetKeyPage( pBasePage, uiFirst - 1 ) != 0 )
|
|
uiFirst--;
|
|
if( uiFirst != uiLast )
|
|
{
|
|
LPPAGEINFO pFirst, pLast;
|
|
|
|
if( uiFirst == uiBaseKey )
|
|
{
|
|
pFirst = pPage;
|
|
pLast = hb_ntxPageLoad( pTag, hb_ntxGetKeyPage( pBasePage, uiLast ) );
|
|
if( ! pLast )
|
|
{
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
hb_ntxPageRelease( pTag, pBasePage );
|
|
if( pNewKey )
|
|
hb_ntxKeyFree( pNewKey );
|
|
return FALSE;
|
|
}
|
|
uiBaseKey = iKey;
|
|
}
|
|
else
|
|
{
|
|
pLast = pPage;
|
|
pFirst = hb_ntxPageLoad( pTag, hb_ntxGetKeyPage( pBasePage, uiFirst ) );
|
|
if( ! pFirst )
|
|
{
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
hb_ntxPageRelease( pTag, pBasePage );
|
|
if( pNewKey )
|
|
hb_ntxKeyFree( pNewKey );
|
|
return FALSE;
|
|
}
|
|
uiBaseKey = pFirst->uiKeys + iKey + 1;
|
|
}
|
|
if( pFirst->uiKeys + pLast->uiKeys <= ( pTag->MaxKeys - 1 ) << 1 )
|
|
{
|
|
hb_ntxBalancePages( pTag, pBasePage, uiFirst, pFirst, pLast );
|
|
if( pFirst->uiKeys >= uiBaseKey )
|
|
hb_ntxPageKeyAdd( pTag, pFirst, uiBaseKey, pKey->Tag, pKey->Xtra, pKey->key );
|
|
else
|
|
hb_ntxPageKeyAdd( pTag, pLast, uiBaseKey - pFirst->uiKeys - 1, pKey->Tag, pKey->Xtra, pKey->key );
|
|
pKey = NULL;
|
|
}
|
|
if( pFirst != pPage )
|
|
hb_ntxPageRelease( pTag, pFirst );
|
|
else
|
|
hb_ntxPageRelease( pTag, pLast );
|
|
hb_ntxPageRelease( pTag, pBasePage );
|
|
if( !pKey )
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
pKey = hb_ntxPageSplit( pTag, pPage, pKey, iKey );
|
|
if( pNewKey )
|
|
hb_ntxKeyFree( pNewKey );
|
|
pNewKey = pKey;
|
|
}
|
|
iLevel--;
|
|
}
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
if( pKey )
|
|
{
|
|
pPage = hb_ntxPageNew( pTag, FALSE );
|
|
if( ! pPage )
|
|
return FALSE;
|
|
hb_ntxPageKeyAdd( pTag, pPage, 0, pKey->Tag, pKey->Xtra, pKey->key );
|
|
hb_ntxSetKeyPage( pPage, 1, pTag->RootBlock );
|
|
pTag->RootBlock = pPage->Page;
|
|
pTag->HdrChanged = TRUE;
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
pTag->stackLevel = 0;
|
|
}
|
|
if( pNewKey )
|
|
hb_ntxKeyFree( pNewKey );
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* del key at the curret page path from the index
|
|
*/
|
|
static BOOL hb_ntxTagKeyDel( LPTAGINFO pTag, LPKEYINFO pKey )
|
|
{
|
|
int iLevel, iBaseKey, iKey;
|
|
LPPAGEINFO pBasePage, pPage;
|
|
ULONG ulPage;
|
|
|
|
pKey->Tag = 0;
|
|
if( pTag->stackLevel == 0 || pTag->CurKeyInfo->Xtra != pKey->Xtra ||
|
|
memcmp( pTag->CurKeyInfo->key, pKey->key, pTag->KeyLength ) != 0 )
|
|
{
|
|
if( ! hb_ntxTagKeyFind( pTag, pKey, pTag->KeyLength ) )
|
|
return FALSE;
|
|
}
|
|
|
|
iLevel = pTag->stackLevel - 1;
|
|
|
|
pPage = hb_ntxPageLoad( pTag, pTag->stack[ iLevel ].page );
|
|
if( ! pPage )
|
|
return FALSE;
|
|
iKey = pTag->stack[ iLevel ].ikey;
|
|
ulPage = hb_ntxGetKeyPage( pPage, iKey );
|
|
|
|
if( ulPage )
|
|
{
|
|
pBasePage = pPage;
|
|
iBaseKey = iKey;
|
|
pPage = hb_ntxPageBottomMove( pTag, ulPage );
|
|
if( ! pPage )
|
|
{
|
|
hb_ntxPageRelease( pTag, pBasePage );
|
|
return FALSE;
|
|
}
|
|
iLevel = pTag->stackLevel - 1;
|
|
iKey = pTag->stack[ iLevel ].ikey;
|
|
|
|
hb_ntxSetKeyRec( pBasePage, iBaseKey, hb_ntxGetKeyRec( pPage, iKey ) );
|
|
memcpy( hb_ntxGetKeyVal( pBasePage, iBaseKey ),
|
|
hb_ntxGetKeyVal( pPage, iKey ), pTag->KeyLength );
|
|
pBasePage->Changed = TRUE;
|
|
#ifdef HB_NTX_DEBUG
|
|
hb_ntxPageCheckKeys( pBasePage, pTag, iBaseKey, 61 );
|
|
#endif
|
|
hb_ntxPageRelease( pTag, pBasePage );
|
|
}
|
|
hb_ntxPageKeyDel( pPage, iKey );
|
|
|
|
while( iLevel > 0 )
|
|
{
|
|
if( pPage->uiKeys < ( pTag->MaxKeys >> 1 ) )
|
|
{
|
|
USHORT uiFirst, uiLast, uiBaseKey;
|
|
|
|
pBasePage = hb_ntxPageLoad( pTag, pTag->stack[ iLevel -1 ].page );
|
|
if( ! pBasePage )
|
|
{
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
return FALSE;
|
|
}
|
|
uiFirst = uiLast = uiBaseKey = pTag->stack[ iLevel -1 ].ikey;
|
|
if( uiLast < pBasePage->uiKeys && hb_ntxGetKeyPage( pBasePage, uiLast + 1 ) != 0 )
|
|
uiLast++;
|
|
else if( uiFirst > 0 && hb_ntxGetKeyPage( pBasePage, uiFirst - 1 ) != 0 )
|
|
uiFirst--;
|
|
|
|
if( uiFirst == uiLast )
|
|
{
|
|
if( pPage->uiKeys == 0 )
|
|
{
|
|
hb_ntxSetKeyPage( pBasePage, uiBaseKey, 0 );
|
|
hb_ntxPageFree( pTag, pPage );
|
|
}
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
}
|
|
else
|
|
{
|
|
LPPAGEINFO pFirst, pLast;
|
|
|
|
if( uiFirst == uiBaseKey )
|
|
{
|
|
pFirst = pPage;
|
|
pLast = hb_ntxPageLoad( pTag, hb_ntxGetKeyPage( pBasePage, uiLast ) );
|
|
if( ! pLast )
|
|
{
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
hb_ntxPageRelease( pTag, pBasePage );
|
|
pTag->stackLevel = 0;
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pLast = pPage;
|
|
pFirst = hb_ntxPageLoad( pTag, hb_ntxGetKeyPage( pBasePage, uiFirst ) );
|
|
if( ! pFirst )
|
|
{
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
hb_ntxPageRelease( pTag, pBasePage );
|
|
pTag->stackLevel = 0;
|
|
return FALSE;
|
|
}
|
|
}
|
|
if( pFirst->uiKeys + pLast->uiKeys < pTag->MaxKeys )
|
|
hb_ntxPageJoin( pTag, pBasePage, uiFirst, pFirst, pLast );
|
|
else
|
|
hb_ntxBalancePages( pTag, pBasePage, uiFirst, pFirst, pLast );
|
|
hb_ntxPageRelease( pTag, pFirst );
|
|
hb_ntxPageRelease( pTag, pLast );
|
|
}
|
|
pPage = pBasePage;
|
|
}
|
|
else
|
|
break;
|
|
iLevel--;
|
|
}
|
|
|
|
if( pPage->uiKeys == 0 && pPage->Page == pTag->RootBlock )
|
|
{
|
|
ulPage = hb_ntxGetKeyPage( pPage, 0 );
|
|
if( ulPage != 0 )
|
|
{
|
|
pTag->RootBlock = ulPage;
|
|
pTag->HdrChanged = TRUE;
|
|
hb_ntxPageFree( pTag, pPage );
|
|
}
|
|
}
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
pTag->stackLevel = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* refresh CurKey value and set proper path from RootPage to LeafPage
|
|
*/
|
|
static BOOL hb_ntxCurKeyRefresh( LPTAGINFO pTag )
|
|
{
|
|
NTXAREAP pArea = pTag->Owner->Owner;
|
|
|
|
if( pArea->lpdbPendingRel )
|
|
SELF_FORCEREL( ( AREAP ) pArea );
|
|
|
|
if( !pArea->fPositioned )
|
|
{
|
|
pTag->stackLevel = 0;
|
|
pTag->TagEOF = TRUE;
|
|
pTag->CurKeyInfo->Xtra = 0;
|
|
return FALSE;
|
|
}
|
|
else if( pTag->stackLevel == 0 || pTag->CurKeyInfo->Xtra != pArea->ulRecNo )
|
|
{
|
|
BYTE buf[ NTX_MAX_KEY ];
|
|
BOOL fBuf = FALSE;
|
|
LPKEYINFO pKey = NULL;
|
|
/* Try to find previous if it's key for the same record */
|
|
if( pTag->CurKeyInfo->Xtra == pArea->ulRecNo )
|
|
{
|
|
fBuf = TRUE;
|
|
memcpy( buf, pTag->CurKeyInfo->key, pTag->KeyLength );
|
|
pKey = hb_ntxKeyCopy( pKey, pTag->CurKeyInfo, pTag->KeyLength );
|
|
hb_ntxTagKeyFind( pTag, pKey, pTag->KeyLength );
|
|
}
|
|
if( pTag->CurKeyInfo->Xtra != pArea->ulRecNo )
|
|
{
|
|
BOOL fValidBuf = pArea->fValidBuffer;
|
|
/* not found, create new key from DBF and if differs seek again */
|
|
pKey = hb_ntxEvalKey( pKey, pTag );
|
|
if( !fBuf || memcmp( buf, pKey->key, pTag->KeyLength ) != 0 )
|
|
{
|
|
hb_ntxTagKeyFind( pTag, pKey, pTag->KeyLength );
|
|
}
|
|
/* not found, if key was generated from DBF buffer then force to
|
|
* update it, create the new key and if differs seek again */
|
|
if( pTag->CurKeyInfo->Xtra != pArea->ulRecNo && fValidBuf )
|
|
{
|
|
SELF_GOTO( ( AREAP ) pArea, pArea->ulRecNo );
|
|
memcpy( buf, pKey->key, pTag->KeyLength );
|
|
pKey = hb_ntxEvalKey( pKey, pTag );
|
|
if( memcmp( buf, pKey->key, pTag->KeyLength ) != 0 )
|
|
hb_ntxTagKeyFind( pTag, pKey, pTag->KeyLength );
|
|
}
|
|
}
|
|
hb_ntxKeyFree( pKey );
|
|
return( pTag->CurKeyInfo->Xtra != 0 && pTag->CurKeyInfo->Xtra == pArea->ulRecNo );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Skip in tag respecting record filter only
|
|
*/
|
|
static void hb_ntxTagSkipFilter( LPTAGINFO pTag, BOOL fForward )
|
|
{
|
|
BOOL fBack, fEof = fForward ? pTag->TagEOF : pTag->TagBOF;
|
|
|
|
fBack = pTag->fUsrDescend == pTag->AscendKey ? fForward : !fForward;
|
|
|
|
while( !fEof && !hb_ntxCheckRecordScope( pTag->Owner->Owner,
|
|
pTag->CurKeyInfo->Xtra ) )
|
|
{
|
|
if( fBack )
|
|
fEof = !hb_ntxTagPrevKey( pTag );
|
|
else
|
|
fEof = !hb_ntxTagNextKey( pTag );
|
|
|
|
if( !fEof && !hb_ntxKeyInScope( pTag, pTag->CurKeyInfo ) )
|
|
{
|
|
fEof = TRUE;
|
|
}
|
|
}
|
|
if( fEof )
|
|
{
|
|
if( fForward )
|
|
pTag->TagEOF = TRUE;
|
|
else
|
|
pTag->TagBOF = TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* go to the first visiable record in Tag
|
|
*/
|
|
static void hb_ntxTagGoTop( LPTAGINFO pTag )
|
|
{
|
|
PHB_NTXSCOPE pScope = pTag->fUsrDescend ? &pTag->bottom : &pTag->top;
|
|
|
|
if( pScope->scopeKeyLen )
|
|
hb_ntxTagKeyFind( pTag, pScope->scopeKey, pScope->scopeKeyLen );
|
|
else if( pTag->fUsrDescend == pTag->AscendKey )
|
|
hb_ntxTagBottomKey( pTag );
|
|
else
|
|
hb_ntxTagTopKey( pTag );
|
|
|
|
pTag->TagEOF = pTag->CurKeyInfo->Xtra == 0 ||
|
|
!hb_ntxKeyInScope( pTag, pTag->CurKeyInfo );
|
|
|
|
if( ! pTag->TagEOF && pTag->Owner->Owner->dbfi.fFilter )
|
|
hb_ntxTagSkipFilter( pTag, TRUE );
|
|
|
|
pTag->TagBOF = pTag->TagEOF;
|
|
}
|
|
|
|
/*
|
|
* go to the last visiable record in Tag
|
|
*/
|
|
static void hb_ntxTagGoBottom( LPTAGINFO pTag )
|
|
{
|
|
PHB_NTXSCOPE pScope = pTag->fUsrDescend ? &pTag->top : &pTag->bottom;
|
|
|
|
if( pScope->scopeKeyLen )
|
|
hb_ntxTagKeyFind( pTag, pScope->scopeKey, pScope->scopeKeyLen );
|
|
else if( pTag->fUsrDescend == pTag->AscendKey )
|
|
hb_ntxTagTopKey( pTag );
|
|
else
|
|
hb_ntxTagBottomKey( pTag );
|
|
|
|
pTag->TagBOF = pTag->CurKeyInfo->Xtra == 0 ||
|
|
!hb_ntxKeyInScope( pTag, pTag->CurKeyInfo );
|
|
|
|
if( ! pTag->TagBOF && pTag->Owner->Owner->dbfi.fFilter )
|
|
hb_ntxTagSkipFilter( pTag, FALSE );
|
|
|
|
pTag->TagEOF = pTag->TagBOF;
|
|
}
|
|
|
|
/*
|
|
* skip to Next Key in the Tag
|
|
*/
|
|
static void hb_ntxTagSkipNext( LPTAGINFO pTag )
|
|
{
|
|
pTag->TagBOF = FALSE;
|
|
|
|
if( pTag->stackLevel == 0 )
|
|
pTag->TagEOF = TRUE;
|
|
else if( ! hb_ntxInTopScope( pTag, pTag->CurKeyInfo->key ) )
|
|
hb_ntxTagGoTop( pTag );
|
|
else if( pTag->fUsrDescend == pTag->AscendKey )
|
|
pTag->TagEOF = !hb_ntxTagPrevKey( pTag );
|
|
else
|
|
pTag->TagEOF = !hb_ntxTagNextKey( pTag );
|
|
|
|
if( ! pTag->TagEOF && ! hb_ntxKeyInScope( pTag, pTag->CurKeyInfo ) )
|
|
pTag->TagEOF = TRUE;
|
|
|
|
if( ! pTag->TagEOF && pTag->Owner->Owner->dbfi.fFilter )
|
|
hb_ntxTagSkipFilter( pTag, TRUE );
|
|
}
|
|
|
|
/*
|
|
* skip to Previous Key in the Tag
|
|
*/
|
|
static void hb_ntxTagSkipPrev( LPTAGINFO pTag )
|
|
{
|
|
pTag->TagEOF = FALSE;
|
|
|
|
if( pTag->stackLevel == 0 )
|
|
/* TODO?: check if this is NTX behavior,
|
|
for sure CDX works in such way */
|
|
hb_ntxTagGoBottom( pTag );
|
|
else if( pTag->fUsrDescend == pTag->AscendKey )
|
|
pTag->TagBOF = !hb_ntxTagNextKey( pTag );
|
|
else
|
|
pTag->TagBOF = !hb_ntxTagPrevKey( pTag );
|
|
|
|
if( ! pTag->TagBOF && ! hb_ntxKeyInScope( pTag, pTag->CurKeyInfo ) )
|
|
pTag->TagBOF = TRUE;
|
|
|
|
if( ! pTag->TagBOF && pTag->Owner->Owner->dbfi.fFilter )
|
|
hb_ntxTagSkipFilter( pTag, FALSE );
|
|
}
|
|
|
|
/*
|
|
* count keys in the given page and all subpages
|
|
*/
|
|
static ULONG hb_ntxPageCountKeys( LPTAGINFO pTag, ULONG ulPage )
|
|
{
|
|
LPPAGEINFO pPage = hb_ntxPageLoad( pTag, ulPage );
|
|
ULONG ulKeys;
|
|
USHORT u;
|
|
|
|
if( ! pPage )
|
|
return 0;
|
|
|
|
ulKeys = pPage->uiKeys;
|
|
for( u = 0; u <= pPage->uiKeys; u++ )
|
|
{
|
|
ulPage = hb_ntxGetKeyPage( pPage, u );
|
|
if( ulPage )
|
|
ulKeys += hb_ntxPageCountKeys( pTag, ulPage );
|
|
}
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
|
|
return ulKeys;
|
|
}
|
|
|
|
/*
|
|
* count relative position of current location in page stack
|
|
*/
|
|
static double hb_ntxTagCountRelKeyPos( LPTAGINFO pTag )
|
|
{
|
|
int iLevel = pTag->stackLevel, iKeys;
|
|
double dPos = 1.0;
|
|
|
|
while( --iLevel >= 0 )
|
|
{
|
|
LPPAGEINFO pPage = hb_ntxPageLoad( pTag, pTag->stack[ iLevel ].page );
|
|
if( ! pPage )
|
|
break;
|
|
iKeys = pPage->uiKeys;
|
|
if( hb_ntxGetKeyPage( pPage, pPage->uiKeys ) )
|
|
++iKeys;
|
|
else if( iLevel == pTag->stackLevel - 1 )
|
|
dPos = 0.5;
|
|
dPos = ( dPos + pTag->stack[ iLevel ].ikey ) / iKeys;
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
}
|
|
if( pTag->fUsrDescend == pTag->AscendKey )
|
|
dPos = 1.0 - dPos;
|
|
return dPos;
|
|
}
|
|
|
|
static void hb_ntxTagGoToRelKeyPos( LPTAGINFO pTag, double dPos )
|
|
{
|
|
LPPAGEINFO pPage = NULL;
|
|
ULONG ulPage = 0;
|
|
int iKey, iKeys;
|
|
|
|
if( pTag->fUsrDescend == pTag->AscendKey )
|
|
dPos = 1.0 - dPos;
|
|
|
|
pTag->stackLevel = 0;
|
|
do
|
|
{
|
|
if( pPage )
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
pPage = hb_ntxPageLoad( pTag, ulPage );
|
|
if( ! pPage )
|
|
{
|
|
pTag->stackLevel = 0;
|
|
return;
|
|
}
|
|
if( pPage->uiKeys == 0 )
|
|
iKey = 0;
|
|
else
|
|
{
|
|
iKeys = pPage->uiKeys;
|
|
if( hb_ntxGetKeyPage( pPage, pPage->uiKeys ) )
|
|
++iKeys;
|
|
iKey = ( int ) ( dPos * iKeys );
|
|
if( iKey >= iKeys )
|
|
iKey = iKeys - 1;
|
|
dPos = dPos * iKeys - iKey;
|
|
if( dPos <= 0.0 )
|
|
dPos = 0.0;
|
|
else if( dPos >= 1.0 )
|
|
dPos = 1.0;
|
|
}
|
|
hb_ntxTagSetPageStack( pTag, pPage->Page, iKey );
|
|
ulPage = hb_ntxGetKeyPage( pPage, iKey );
|
|
} while( ulPage != 0 );
|
|
|
|
hb_ntxPageGetKey( pPage, iKey, pTag->CurKeyInfo, pTag->KeyLength );
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
|
|
if( dPos > 0.75 )
|
|
hb_ntxTagNextKey( pTag );
|
|
else if( dPos < 0.25 )
|
|
hb_ntxTagPrevKey( pTag );
|
|
}
|
|
|
|
/*
|
|
* free pages allocated by tag
|
|
*/
|
|
static BOOL hb_ntxTagPagesFree( LPTAGINFO pTag, ULONG ulPage )
|
|
{
|
|
LPPAGEINFO pPage = hb_ntxPageLoad( pTag, ulPage );
|
|
BOOL fOK = pPage != NULL;
|
|
USHORT u;
|
|
|
|
for( u = 0; fOK && u <= pPage->uiKeys; u++ )
|
|
{
|
|
ulPage = hb_ntxGetKeyPage( pPage, u );
|
|
if( ulPage )
|
|
fOK = hb_ntxTagPagesFree( pTag, ulPage );
|
|
}
|
|
|
|
if( fOK )
|
|
{
|
|
pPage->uiKeys = 0;
|
|
hb_ntxPageFree( pTag, pPage );
|
|
if( !pPage->pPrev )
|
|
fOK = hb_ntxPageSave( pTag->Owner, pPage );
|
|
}
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
|
|
return fOK;
|
|
}
|
|
|
|
/*
|
|
* free space allocated by tag
|
|
*/
|
|
static ERRCODE hb_ntxTagSpaceFree( LPTAGINFO pTag )
|
|
{
|
|
if( hb_ntxTagHeaderCheck( pTag ) )
|
|
{
|
|
if( pTag->RootBlock )
|
|
{
|
|
if( ! hb_ntxTagPagesFree( pTag, pTag->RootBlock ) )
|
|
return FAILURE;
|
|
}
|
|
hb_ntxPageAddFree( pTag, pTag->HeadBlock );
|
|
hb_ntxIndexTagDel( pTag->Owner, pTag->TagName );
|
|
pTag->Owner->Changed = TRUE;
|
|
}
|
|
hb_ntxTagDelete( pTag );
|
|
return SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* create index file name
|
|
*/
|
|
static void hb_ntxCreateFName( NTXAREAP pArea, char * szBagName, BOOL * fProd,
|
|
char * szFileName, char * szTagName )
|
|
{
|
|
PHB_FNAME pFileName;
|
|
PHB_ITEM pExt = NULL;
|
|
BOOL fName = szBagName && *szBagName;
|
|
|
|
pFileName = hb_fsFNameSplit( fName ? szBagName : pArea->szDataFileName );
|
|
|
|
if( szTagName )
|
|
{
|
|
if( pFileName->szName )
|
|
hb_strncpyUpperTrim( szTagName, pFileName->szName, NTX_MAX_TAGNAME );
|
|
else
|
|
szTagName[ 0 ] = '\0';
|
|
}
|
|
|
|
if( !pFileName->szExtension || !fName )
|
|
{
|
|
DBORDERINFO pExtInfo;
|
|
memset( &pExtInfo, 0, sizeof( pExtInfo ) );
|
|
pExt = pExtInfo.itmResult = hb_itemPutC( NULL, "" );
|
|
if( SELF_ORDINFO( ( AREAP ) pArea, DBOI_BAGEXT, &pExtInfo ) == SUCCESS &&
|
|
hb_itemGetCLen( pExt ) > 0 )
|
|
{
|
|
pFileName->szExtension = hb_itemGetCPtr( pExt );
|
|
}
|
|
}
|
|
hb_fsFNameMerge( szFileName, pFileName );
|
|
|
|
if( fProd )
|
|
{
|
|
if( ! pFileName->szName )
|
|
*fProd = FALSE;
|
|
else if( !fName )
|
|
*fProd = TRUE;
|
|
else
|
|
{
|
|
PHB_FNAME pTableFileName = hb_fsFNameSplit( pArea->szDataFileName );
|
|
|
|
*fProd = pTableFileName->szName &&
|
|
hb_stricmp( pTableFileName->szName, pFileName->szName ) == 0;
|
|
if( *fProd && pFileName->szExtension && ! pExt )
|
|
{
|
|
DBORDERINFO pExtInfo;
|
|
memset( &pExtInfo, 0, sizeof( pExtInfo ) );
|
|
pExt = pExtInfo.itmResult = hb_itemPutC( NULL, "" );
|
|
if( SELF_ORDINFO( ( AREAP ) pArea, DBOI_BAGEXT, &pExtInfo ) == SUCCESS )
|
|
{
|
|
*fProd = hb_stricmp( pFileName->szExtension,
|
|
hb_itemGetCPtr( pExt ) ) == 0;
|
|
}
|
|
}
|
|
hb_xfree( pTableFileName );
|
|
}
|
|
}
|
|
hb_xfree( pFileName );
|
|
if( pExt )
|
|
hb_itemRelease( pExt );
|
|
}
|
|
|
|
/*
|
|
* find order bag by its name
|
|
*/
|
|
static LPNTXINDEX hb_ntxFindBag( NTXAREAP pArea, char * szBagName )
|
|
{
|
|
LPNTXINDEX pIndex;
|
|
PHB_FNAME pSeek, pName;
|
|
BOOL fFound;
|
|
|
|
pSeek = hb_fsFNameSplit( szBagName );
|
|
if( ! pSeek->szName )
|
|
pSeek->szName = "";
|
|
|
|
pIndex = pArea->lpIndexes;
|
|
while( pIndex )
|
|
{
|
|
pName = hb_fsFNameSplit( pIndex->IndexName );
|
|
if( ! pName->szName )
|
|
pName->szName = "";
|
|
fFound = !hb_stricmp( pName->szName, pSeek->szName ) &&
|
|
( !pSeek->szPath || ( pName->szPath &&
|
|
!hb_stricmp( pName->szPath, pSeek->szPath ) ) ) &&
|
|
( !pSeek->szExtension || ( pName->szExtension &&
|
|
!hb_stricmp( pName->szExtension, pSeek->szExtension ) ) );
|
|
hb_xfree( pName );
|
|
if( fFound )
|
|
break;
|
|
pIndex = pIndex->pNext;
|
|
}
|
|
hb_xfree( pSeek );
|
|
return pIndex;
|
|
}
|
|
|
|
/*
|
|
* Find tag by name in index bag
|
|
*/
|
|
static int hb_ntxFindTagByName( LPNTXINDEX pIndex, char * szTag )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < pIndex->iTags; i++ )
|
|
{
|
|
if( !hb_strnicmp( pIndex->lpTags[ i ]->TagName, szTag,
|
|
NTX_MAX_TAGNAME ) )
|
|
return i + 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Find the tag by its name or number
|
|
*/
|
|
static LPTAGINFO hb_ntxFindTag( NTXAREAP pArea, PHB_ITEM pTagItem,
|
|
PHB_ITEM pBagItem )
|
|
{
|
|
LPNTXINDEX pIndex;
|
|
BOOL fBag;
|
|
|
|
if( ! pTagItem ||
|
|
( hb_itemType( pTagItem ) & ( HB_IT_STRING | HB_IT_NUMERIC ) ) == 0 )
|
|
return pArea->lpCurTag;
|
|
|
|
fBag = hb_itemGetCLen( pBagItem ) > 0;
|
|
if( fBag )
|
|
{
|
|
if( hb_itemType( pTagItem ) & HB_IT_STRING )
|
|
pIndex = hb_ntxFindBag( pArea, hb_itemGetCPtr( pBagItem ) );
|
|
else
|
|
pIndex = pArea->lpIndexes;
|
|
}
|
|
else
|
|
{
|
|
int iBag = hb_itemGetNI( pBagItem );
|
|
|
|
pIndex = pArea->lpIndexes;
|
|
if( iBag > 0 )
|
|
{
|
|
fBag = TRUE;
|
|
while( pIndex )
|
|
{
|
|
if( --iBag == 0 )
|
|
break;
|
|
pIndex = pIndex->pNext;
|
|
}
|
|
}
|
|
else if( iBag < 0 )
|
|
{
|
|
pIndex = NULL;
|
|
}
|
|
}
|
|
if( pIndex )
|
|
{
|
|
if( hb_itemType( pTagItem ) & HB_IT_STRING )
|
|
{
|
|
char * szTag = hb_itemGetCPtr( pTagItem );
|
|
int iTag;
|
|
|
|
if( fBag )
|
|
iTag = hb_ntxFindTagByName( pIndex, szTag );
|
|
else
|
|
{
|
|
do
|
|
{
|
|
iTag = hb_ntxFindTagByName( pIndex, szTag );
|
|
if( iTag )
|
|
break;
|
|
pIndex = pIndex->pNext;
|
|
} while( pIndex );
|
|
}
|
|
if( iTag )
|
|
return pIndex->lpTags[ iTag - 1 ];
|
|
}
|
|
else
|
|
{
|
|
int i = hb_itemGetNI( pTagItem ) - 1;
|
|
|
|
if( i >= 0 )
|
|
{
|
|
if( fBag )
|
|
{
|
|
if( i < pIndex->iTags )
|
|
return pIndex->lpTags[ i ];
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
if( i < pIndex->iTags )
|
|
return pIndex->lpTags[ i ];
|
|
i -= pIndex->iTags;
|
|
pIndex = pIndex->pNext;
|
|
} while( pIndex );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* find the given tag number
|
|
*/
|
|
static int hb_ntxFindTagNum( NTXAREAP pArea, LPTAGINFO pTag )
|
|
{
|
|
if( pArea->fSetTagNumbers )
|
|
{
|
|
LPNTXINDEX pIndex = pArea->lpIndexes;
|
|
USHORT uiNum = 0, i;
|
|
|
|
pTag->uiNumber = 0;
|
|
while( pIndex )
|
|
{
|
|
for( i = 0; i < pIndex->iTags; i++ )
|
|
{
|
|
pIndex->lpTags[ i ]->uiNumber = ++uiNum;
|
|
}
|
|
pIndex = pIndex->pNext;
|
|
}
|
|
pArea->fSetTagNumbers = FALSE;
|
|
}
|
|
return pTag->uiNumber;
|
|
}
|
|
|
|
/*
|
|
* count number of keys in given tag
|
|
*/
|
|
static ULONG hb_ntxOrdKeyCount( LPTAGINFO pTag )
|
|
{
|
|
ULONG ulKeyCount = 0;
|
|
|
|
if( !pTag->Owner->fShared && pTag->keyCount &&
|
|
!pTag->Owner->Owner->dbfi.fFilter )
|
|
return pTag->keyCount;
|
|
|
|
if( hb_ntxTagLockRead( pTag ) )
|
|
{
|
|
hb_ntxTagRefreshScope( pTag );
|
|
|
|
if( pTag->top.scopeKeyLen || pTag->bottom.scopeKeyLen ||
|
|
pTag->Owner->Owner->dbfi.fFilter )
|
|
{
|
|
hb_ntxTagGoTop( pTag );
|
|
while( !pTag->TagEOF )
|
|
{
|
|
ulKeyCount++;
|
|
hb_ntxTagSkipNext( pTag );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ulKeyCount = hb_ntxPageCountKeys( pTag, 0 );
|
|
}
|
|
if( !pTag->Owner->Owner->dbfi.fFilter )
|
|
pTag->keyCount = ulKeyCount;
|
|
hb_ntxTagUnLockRead( pTag );
|
|
}
|
|
return ulKeyCount;
|
|
}
|
|
|
|
/*
|
|
* get the logical key position in the given tag
|
|
*/
|
|
static ULONG hb_ntxOrdKeyNo( LPTAGINFO pTag )
|
|
{
|
|
ULONG ulKeyNo = 0;
|
|
|
|
if( hb_ntxTagLockRead( pTag ) )
|
|
{
|
|
hb_ntxTagRefreshScope( pTag );
|
|
if( hb_ntxCurKeyRefresh( pTag ) )
|
|
{
|
|
if( pTag->top.scopeKeyLen || pTag->bottom.scopeKeyLen ||
|
|
pTag->Owner->Owner->dbfi.fFilter )
|
|
{
|
|
if( hb_ntxKeyInScope( pTag, pTag->CurKeyInfo ) )
|
|
{
|
|
do
|
|
{
|
|
ulKeyNo++;
|
|
hb_ntxTagSkipPrev( pTag );
|
|
}
|
|
while( !pTag->TagBOF );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int iLevel = pTag->stackLevel, iKey, iFirst = 1;
|
|
BOOL fBack = pTag->fUsrDescend == pTag->AscendKey;
|
|
LPPAGEINFO pPage;
|
|
ULONG ulPage;
|
|
|
|
while( --iLevel >= 0 )
|
|
{
|
|
pPage = hb_ntxPageLoad( pTag, pTag->stack[ iLevel ].page );
|
|
if( ! pPage )
|
|
break;
|
|
if( fBack )
|
|
{
|
|
iKey = pTag->stack[ iLevel ].ikey;
|
|
ulKeyNo += pPage->uiKeys - iKey;
|
|
while( ++iKey <= pPage->uiKeys )
|
|
{
|
|
ulPage = hb_ntxGetKeyPage( pPage, iKey );
|
|
if( ulPage )
|
|
ulKeyNo += hb_ntxPageCountKeys( pTag, ulPage );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ulKeyNo += iKey = pTag->stack[ iLevel ].ikey + iFirst;
|
|
iFirst = 0;
|
|
while( --iKey >= 0 )
|
|
{
|
|
ulPage = hb_ntxGetKeyPage( pPage, iKey );
|
|
if( ulPage )
|
|
ulKeyNo += hb_ntxPageCountKeys( pTag, ulPage );
|
|
}
|
|
}
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
}
|
|
}
|
|
}
|
|
hb_ntxTagUnLockRead( pTag );
|
|
}
|
|
return ulKeyNo;
|
|
}
|
|
|
|
/*
|
|
* set logical key position in given tag
|
|
*/
|
|
static BOOL hb_ntxOrdKeyGoto( LPTAGINFO pTag, ULONG ulKeyNo )
|
|
{
|
|
if( ! ulKeyNo || ! hb_ntxTagLockRead( pTag ) )
|
|
return FALSE;
|
|
hb_ntxTagRefreshScope( pTag );
|
|
hb_ntxTagGoTop( pTag );
|
|
while( !pTag->TagEOF && --ulKeyNo )
|
|
{
|
|
hb_ntxTagSkipNext( pTag );
|
|
}
|
|
hb_ntxTagUnLockRead( pTag );
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* get the relative key position (from 0.0 to 1.0) in the given tag
|
|
*/
|
|
static double hb_ntxOrdGetRelKeyPos( LPTAGINFO pTag )
|
|
{
|
|
double dPos = 0.0, dStart = 0.0, dStop = 1.0, dFact = 0.0000000000001;
|
|
BOOL fOK = TRUE, fFilter = pTag->Owner->Owner->dbfi.fFilter;
|
|
|
|
if( ! hb_ntxTagLockRead( pTag ) )
|
|
return FALSE;
|
|
|
|
hb_ntxTagRefreshScope( pTag );
|
|
|
|
pTag->Owner->Owner->dbfi.fFilter = FALSE;
|
|
if( pTag->fUsrDescend ? pTag->bottom.scopeKeyLen : pTag->top.scopeKeyLen )
|
|
{
|
|
hb_ntxTagGoTop( pTag );
|
|
if( pTag->TagEOF )
|
|
fOK = FALSE;
|
|
else
|
|
dStart = hb_ntxTagCountRelKeyPos( pTag );
|
|
}
|
|
if( fOK && ( pTag->fUsrDescend ? pTag->top.scopeKeyLen : pTag->bottom.scopeKeyLen ) )
|
|
{
|
|
hb_ntxTagGoBottom( pTag );
|
|
if( pTag->TagBOF )
|
|
fOK = FALSE;
|
|
else
|
|
dStop = hb_ntxTagCountRelKeyPos( pTag );
|
|
}
|
|
pTag->Owner->Owner->dbfi.fFilter = fFilter;
|
|
|
|
if( fOK )
|
|
{
|
|
if( hb_ntxCurKeyRefresh( pTag ) &&
|
|
hb_ntxKeyInScope( pTag, pTag->CurKeyInfo ) )
|
|
{
|
|
if( dStart >= dStop - dFact )
|
|
dPos = 0.5;
|
|
else
|
|
{
|
|
dPos = hb_ntxTagCountRelKeyPos( pTag );
|
|
dPos = ( dPos - dStart ) / ( dStop - dStart );
|
|
/* fix possible differences in FL representation */
|
|
if( dPos <= 0.0 )
|
|
dPos = 0.0;
|
|
else if( dPos >= 1.0 )
|
|
dPos = 1.0;
|
|
}
|
|
}
|
|
}
|
|
hb_ntxTagUnLockRead( pTag );
|
|
|
|
return dPos;
|
|
}
|
|
|
|
/*
|
|
* set the relative key position (from 0.0 to 1.0) in the given tag
|
|
*/
|
|
static void hb_ntxOrdSetRelKeyPos( LPTAGINFO pTag, double dPos )
|
|
{
|
|
if( hb_ntxTagLockRead( pTag ) )
|
|
{
|
|
NTXAREAP pArea = pTag->Owner->Owner;
|
|
double dStart = 0.0, dStop = 1.0, dFact = 0.0000000000001;
|
|
BOOL fOK = TRUE, fFilter = pArea->dbfi.fFilter;
|
|
BOOL fForward = TRUE, fTop = FALSE;
|
|
|
|
hb_ntxTagRefreshScope( pTag );
|
|
|
|
if( dPos >= 1.0 )
|
|
fForward = FALSE;
|
|
else if( dPos <= 0.0 )
|
|
fTop = TRUE;
|
|
else
|
|
{
|
|
pArea->dbfi.fFilter = FALSE;
|
|
if( pTag->fUsrDescend ? pTag->bottom.scopeKeyLen : pTag->top.scopeKeyLen )
|
|
{
|
|
hb_ntxTagGoTop( pTag );
|
|
if( pTag->TagEOF )
|
|
fOK = FALSE;
|
|
else
|
|
dStart = hb_ntxTagCountRelKeyPos( pTag );
|
|
}
|
|
if( fOK && ( pTag->fUsrDescend ? pTag->top.scopeKeyLen : pTag->bottom.scopeKeyLen ) )
|
|
{
|
|
hb_ntxTagGoBottom( pTag );
|
|
if( pTag->TagBOF )
|
|
fOK = FALSE;
|
|
else
|
|
dStop = hb_ntxTagCountRelKeyPos( pTag );
|
|
}
|
|
pArea->dbfi.fFilter = fFilter;
|
|
|
|
if( fOK )
|
|
{
|
|
if( dStart >= dStop - dFact )
|
|
{
|
|
fTop = TRUE;
|
|
}
|
|
else
|
|
{
|
|
dPos = dPos * ( dStop - dStart ) + dStart;
|
|
hb_ntxTagGoToRelKeyPos( pTag, dPos );
|
|
if( pTag->CurKeyInfo->Xtra == 0 )
|
|
fForward = FALSE;
|
|
else if( !hb_ntxInTopScope( pTag, pTag->CurKeyInfo->key ) )
|
|
fTop = TRUE;
|
|
else if( !hb_ntxInBottomScope( pTag, pTag->CurKeyInfo->key ) )
|
|
fForward = FALSE;
|
|
}
|
|
}
|
|
}
|
|
if( !fOK )
|
|
{
|
|
SELF_GOTO( ( AREAP ) pArea, 0 );
|
|
}
|
|
else
|
|
{
|
|
LPTAGINFO pSavedTag = pArea->lpCurTag;
|
|
pArea->lpCurTag = pTag;
|
|
|
|
pArea->fTop = pArea->fBottom = FALSE;
|
|
|
|
if( fForward )
|
|
{
|
|
if( fTop )
|
|
hb_ntxTagGoTop( pTag );
|
|
if( pTag->CurKeyInfo->Xtra != 0 )
|
|
{
|
|
SELF_GOTO( ( AREAP ) pArea, pTag->CurKeyInfo->Xtra );
|
|
SELF_SKIPFILTER( ( AREAP ) pArea, 1 );
|
|
if( pArea->fEof && !fTop )
|
|
fForward = FALSE;
|
|
}
|
|
else if( fTop )
|
|
SELF_GOTO( ( AREAP ) pArea, 0 );
|
|
else
|
|
fForward = FALSE;
|
|
}
|
|
if( !fForward )
|
|
{
|
|
hb_ntxTagGoBottom( pTag );
|
|
SELF_GOTO( ( AREAP ) pArea, pTag->CurKeyInfo->Xtra );
|
|
if( pTag->CurKeyInfo->Xtra != 0 )
|
|
{
|
|
pArea->fBottom = TRUE;
|
|
SELF_SKIPFILTER( ( AREAP ) pArea, -1 );
|
|
}
|
|
}
|
|
pArea->lpCurTag = pSavedTag;
|
|
}
|
|
hb_ntxTagUnLockRead( pTag );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* skip to next/previous unique key
|
|
*/
|
|
static BOOL hb_ntxOrdSkipUnique( LPTAGINFO pTag, LONG lDir )
|
|
{
|
|
NTXAREAP pArea = pTag->Owner->Owner;
|
|
BOOL fOut = FALSE, fEof = FALSE, fForward = ( lDir >= 0 );
|
|
|
|
if( pArea->lpdbPendingRel )
|
|
SELF_FORCEREL( ( AREAP ) pArea );
|
|
|
|
pArea->fTop = pArea->fBottom = FALSE;
|
|
|
|
if( hb_ntxTagLockRead( pTag ) )
|
|
{
|
|
LPTAGINFO pSavedTag = pArea->lpCurTag;
|
|
pArea->lpCurTag = pTag;
|
|
|
|
hb_ntxTagRefreshScope( pTag );
|
|
if( hb_ntxCurKeyRefresh( pTag ) )
|
|
{
|
|
char keyVal[ NTX_MAX_KEY ];
|
|
memcpy( keyVal, pTag->CurKeyInfo->key, pTag->KeyLength );
|
|
|
|
do
|
|
{
|
|
if( fForward )
|
|
hb_ntxTagSkipNext( pTag );
|
|
else
|
|
hb_ntxTagSkipPrev( pTag );
|
|
fOut = pTag->TagEOF || pTag->TagBOF;
|
|
}
|
|
while( !fOut && hb_ntxValCompare( pTag,
|
|
pTag->CurKeyInfo->key, pTag->KeyLength,
|
|
keyVal, pTag->KeyLength, TRUE ) == 0 );
|
|
}
|
|
else if( !fForward && !pArea->fPositioned )
|
|
{
|
|
hb_ntxTagGoBottom( pTag );
|
|
fEof = pTag->TagEOF;
|
|
}
|
|
else
|
|
{
|
|
fOut = TRUE;
|
|
}
|
|
if( fOut )
|
|
{
|
|
if( fForward )
|
|
fEof = TRUE;
|
|
else
|
|
{
|
|
hb_ntxTagGoTop( pTag );
|
|
fEof = pTag->TagEOF;
|
|
}
|
|
}
|
|
hb_ntxTagUnLockRead( pTag );
|
|
|
|
SELF_GOTO( ( AREAP ) pArea, fEof ? 0 : pTag->CurKeyInfo->Xtra );
|
|
if( !fEof )
|
|
{
|
|
SELF_SKIPFILTER( ( AREAP ) pArea, ( fForward || fOut ) ? 1 : -1 );
|
|
if( ! fForward && fOut )
|
|
pArea->fBof = TRUE;
|
|
}
|
|
|
|
/* Update Bof and Eof flags */
|
|
if( fForward )
|
|
pArea->fBof = FALSE;
|
|
else
|
|
pArea->fEof = FALSE;
|
|
|
|
pArea->lpCurTag = pSavedTag;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* skip while code block doesn't return TRUE
|
|
*/
|
|
static BOOL hb_ntxOrdSkipEval( LPTAGINFO pTag, BOOL fForward, PHB_ITEM pEval )
|
|
{
|
|
NTXAREAP pArea = pTag->Owner->Owner;
|
|
BOOL fFound = FALSE;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("hb_ntxOrdSkipEval(%p, %d, %p)", pTag, fForward, pEval));
|
|
|
|
if( hb_itemType( pEval ) != HB_IT_BLOCK )
|
|
{
|
|
if( SELF_SKIP( ( AREAP ) pArea, fForward ? 1 : -1 ) != SUCCESS )
|
|
return FALSE;
|
|
return fForward ? !pArea->fEof : !pArea->fBof;
|
|
}
|
|
|
|
if( pArea->lpdbPendingRel )
|
|
SELF_FORCEREL( ( AREAP ) pArea );
|
|
|
|
pArea->fTop = pArea->fBottom = FALSE;
|
|
|
|
if( hb_ntxTagLockRead( pTag ) )
|
|
{
|
|
LPTAGINFO pSavedTag = pArea->lpCurTag;
|
|
pArea->lpCurTag = pTag;
|
|
|
|
hb_ntxTagRefreshScope( pTag );
|
|
if( hb_ntxCurKeyRefresh( pTag ) )
|
|
{
|
|
if( fForward )
|
|
hb_ntxTagSkipNext( pTag );
|
|
else
|
|
hb_ntxTagSkipPrev( pTag );
|
|
|
|
while( fForward ? !pTag->TagEOF : !pTag->TagBOF )
|
|
{
|
|
if( SELF_GOTO( ( AREAP ) pArea, pTag->CurKeyInfo->Xtra ) != SUCCESS )
|
|
break;
|
|
if( hb_ntxEvalSeekCond( pTag, pEval ) )
|
|
{
|
|
ULONG ulRecNo = pArea->ulRecNo;
|
|
SELF_SKIPFILTER( ( AREAP ) pArea, fForward ? 1 : -1 );
|
|
if( pArea->ulRecNo == ulRecNo || hb_ntxEvalSeekCond( pTag, pEval ) )
|
|
{
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if( fForward )
|
|
hb_ntxTagSkipNext( pTag );
|
|
else
|
|
hb_ntxTagSkipPrev( pTag );
|
|
}
|
|
if( !fFound )
|
|
{
|
|
if( fForward )
|
|
SELF_GOTO( ( AREAP ) pArea, 0 );
|
|
else
|
|
{
|
|
SELF_GOTOP( ( AREAP ) pArea );
|
|
pArea->fBof = TRUE;
|
|
}
|
|
}
|
|
}
|
|
pArea->lpCurTag = pSavedTag;
|
|
hb_ntxTagUnLockRead( pTag );
|
|
}
|
|
|
|
/* Update Bof and Eof flags */
|
|
if( fForward )
|
|
pArea->fBof = FALSE;
|
|
else
|
|
pArea->fEof = FALSE;
|
|
|
|
return fFound;
|
|
}
|
|
|
|
/*
|
|
* skip while code block doesn't return TRUE
|
|
*/
|
|
static BOOL hb_ntxOrdSkipWild( LPTAGINFO pTag, BOOL fForward, PHB_ITEM pWildItm )
|
|
{
|
|
NTXAREAP pArea = pTag->Owner->Owner;
|
|
char *szPattern = hb_itemGetCPtr( pWildItm );
|
|
BOOL fFound = FALSE;
|
|
int iFixed = 0;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("hb_ntxOrdSkipWild(%p, %d, %p)", pTag, fForward, pWildItm));
|
|
|
|
if( pTag->KeyType != 'C' || !szPattern || !*szPattern )
|
|
{
|
|
if( SELF_SKIP( ( AREAP ) pArea, fForward ? 1 : -1 ) != SUCCESS )
|
|
return FALSE;
|
|
return fForward ? !pArea->fEof : !pArea->fBof;
|
|
}
|
|
|
|
#ifndef HB_CDP_SUPPORT_OFF
|
|
if( pArea->cdPage != hb_cdp_page )
|
|
{
|
|
szPattern = hb_strdup( szPattern );
|
|
hb_cdpTranslate( szPattern, hb_cdp_page, pArea->cdPage );
|
|
}
|
|
#endif
|
|
while( iFixed < pTag->KeyLength && szPattern[ iFixed ] &&
|
|
szPattern[ iFixed ] != '*' && szPattern[ iFixed ] != '?' )
|
|
{
|
|
++iFixed;
|
|
}
|
|
|
|
if( pArea->lpdbPendingRel )
|
|
SELF_FORCEREL( ( AREAP ) pArea );
|
|
|
|
pArea->fTop = pArea->fBottom = FALSE;
|
|
|
|
if( hb_ntxTagLockRead( pTag ) )
|
|
{
|
|
LPTAGINFO pSavedTag = pArea->lpCurTag;
|
|
pArea->lpCurTag = pTag;
|
|
|
|
hb_ntxTagRefreshScope( pTag );
|
|
if( hb_ntxCurKeyRefresh( pTag ) )
|
|
{
|
|
int iStop = fForward ? -1 : 1;
|
|
if( pTag->fUsrDescend )
|
|
iStop = -iStop;
|
|
if( iFixed && hb_ntxValCompare( pTag, szPattern, iFixed,
|
|
pTag->CurKeyInfo->key, iFixed, FALSE ) == -iStop )
|
|
{
|
|
LPKEYINFO pKey;
|
|
pKey = hb_ntxKeyNew( NULL, pTag->KeyLength );
|
|
memcpy( pKey->key, szPattern, iFixed );
|
|
pKey->key[ iFixed ] = '\0';
|
|
pKey->Xtra = pArea->lpCurTag->fUsrDescend ==
|
|
pArea->lpCurTag->AscendKey ? NTX_MAX_REC_NUM :
|
|
NTX_IGNORE_REC_NUM;
|
|
if( !hb_ntxTagKeyFind( pTag, pKey, iFixed ) )
|
|
{
|
|
if( fForward )
|
|
pTag->TagEOF = TRUE;
|
|
else
|
|
pTag->TagBOF = TRUE;
|
|
}
|
|
hb_ntxKeyFree( pKey );
|
|
}
|
|
else if( fForward )
|
|
hb_ntxTagSkipNext( pTag );
|
|
else
|
|
hb_ntxTagSkipPrev( pTag );
|
|
|
|
while( fForward ? !pTag->TagEOF : !pTag->TagBOF )
|
|
{
|
|
if( hb_strMatchWild( pTag->CurKeyInfo->key, szPattern ) )
|
|
{
|
|
ULONG ulRecNo = pTag->CurKeyInfo->Xtra;
|
|
if( SELF_GOTO( ( AREAP ) pArea, ulRecNo ) != SUCCESS )
|
|
break;
|
|
SELF_SKIPFILTER( ( AREAP ) pArea, fForward ? 1 : -1 );
|
|
if( pArea->ulRecNo == ulRecNo ||
|
|
hb_strMatchWild( pTag->CurKeyInfo->key, szPattern ) )
|
|
{
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if( iFixed && hb_ntxValCompare( pTag, szPattern, iFixed,
|
|
pTag->CurKeyInfo->key, iFixed, FALSE ) == iStop )
|
|
{
|
|
break;
|
|
}
|
|
if( fForward )
|
|
hb_ntxTagSkipNext( pTag );
|
|
else
|
|
hb_ntxTagSkipPrev( pTag );
|
|
}
|
|
if( !fFound )
|
|
{
|
|
if( fForward )
|
|
SELF_GOTO( ( AREAP ) pArea, 0 );
|
|
else
|
|
{
|
|
SELF_GOTOP( ( AREAP ) pArea );
|
|
pArea->fBof = TRUE;
|
|
}
|
|
}
|
|
}
|
|
pArea->lpCurTag = pSavedTag;
|
|
hb_ntxTagUnLockRead( pTag );
|
|
}
|
|
|
|
/* Update Bof and Eof flags */
|
|
if( fForward )
|
|
pArea->fBof = FALSE;
|
|
else
|
|
pArea->fEof = FALSE;
|
|
|
|
#ifndef HB_CDP_SUPPORT_OFF
|
|
if( pArea->cdPage != hb_cdp_page )
|
|
{
|
|
hb_xfree( szPattern );
|
|
}
|
|
#endif
|
|
|
|
return fFound;
|
|
}
|
|
|
|
#if defined(__XHARBOUR__)
|
|
|
|
static BOOL hb_ntxRegexMatch( LPTAGINFO pTag, PHB_REGEX pRegEx, char * szKey )
|
|
{
|
|
#ifndef HB_CDP_SUPPORT_OFF
|
|
char szBuff[ NTX_MAX_KEY + 1 ];
|
|
|
|
if( pTag->Owner->Owner->cdPage != hb_cdp_page )
|
|
{
|
|
hb_strncpy( szBuff, pTag->CurKeyInfo->key, pTag->KeyLength );
|
|
hb_cdpnTranslate( szBuff, pTag->Owner->Owner->cdPage, hb_cdp_page, pTag->KeyLength );
|
|
szKey = szBuff;
|
|
}
|
|
#else
|
|
HB_SYMBOL_UNUSED( pTag );
|
|
#endif
|
|
return hb_regexMatch( pRegEx, szKey, FALSE );
|
|
}
|
|
|
|
/*
|
|
* skip while regular expression on index key val doesn't return TRUE
|
|
*/
|
|
static BOOL hb_ntxOrdSkipRegEx( LPTAGINFO pTag, BOOL fForward, PHB_ITEM pRegExItm )
|
|
{
|
|
NTXAREAP pArea = pTag->Owner->Owner;
|
|
BOOL fFound = FALSE;
|
|
HB_REGEX RegEx;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("hb_ntxOrdSkipRegEx(%p, %d, %p)", pTag, fForward, pRegExItm));
|
|
|
|
if( pTag->KeyType != 'C' || !hb_regexGet( &RegEx, pRegExItm, 0, 0 ) )
|
|
{
|
|
if( SELF_SKIP( ( AREAP ) pArea, fForward ? 1 : -1 ) != SUCCESS )
|
|
return FALSE;
|
|
return fForward ? !pArea->fEof : !pArea->fBof;
|
|
}
|
|
|
|
if( pArea->lpdbPendingRel )
|
|
SELF_FORCEREL( ( AREAP ) pArea );
|
|
|
|
pArea->fTop = pArea->fBottom = FALSE;
|
|
|
|
if( hb_ntxTagLockRead( pTag ) )
|
|
{
|
|
LPTAGINFO pSavedTag = pArea->lpCurTag;
|
|
pArea->lpCurTag = pTag;
|
|
|
|
hb_ntxTagRefreshScope( pTag );
|
|
if( hb_ntxCurKeyRefresh( pTag ) )
|
|
{
|
|
if( fForward )
|
|
hb_ntxTagSkipNext( pTag );
|
|
else
|
|
hb_ntxTagSkipPrev( pTag );
|
|
|
|
while( fForward ? !pTag->TagEOF : !pTag->TagBOF )
|
|
{
|
|
if( SELF_GOTO( ( AREAP ) pArea, pTag->CurKeyInfo->Xtra ) != SUCCESS )
|
|
break;
|
|
|
|
if( hb_ntxRegexMatch( pTag, &RegEx, ( char * ) pTag->CurKeyInfo->key ) )
|
|
{
|
|
ULONG ulRecNo = pArea->ulRecNo;
|
|
SELF_SKIPFILTER( ( AREAP ) pArea, fForward ? 1 : -1 );
|
|
if( pArea->ulRecNo == ulRecNo ||
|
|
hb_ntxRegexMatch( pTag, &RegEx, ( char * ) pTag->CurKeyInfo->key ) )
|
|
{
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if( fForward )
|
|
hb_ntxTagSkipNext( pTag );
|
|
else
|
|
hb_ntxTagSkipPrev( pTag );
|
|
}
|
|
if( !fFound )
|
|
{
|
|
if( fForward )
|
|
SELF_GOTO( ( AREAP ) pArea, 0 );
|
|
else
|
|
{
|
|
SELF_GOTOP( ( AREAP ) pArea );
|
|
pArea->fBof = TRUE;
|
|
}
|
|
}
|
|
}
|
|
pArea->lpCurTag = pSavedTag;
|
|
hb_ntxTagUnLockRead( pTag );
|
|
}
|
|
|
|
/* Update Bof and Eof flags */
|
|
if( fForward )
|
|
pArea->fBof = FALSE;
|
|
else
|
|
pArea->fEof = FALSE;
|
|
|
|
hb_regexFree( &RegEx );
|
|
|
|
return fFound;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* add key to custom tag (ordKeyAdd())
|
|
* user key value is not implemented
|
|
*/
|
|
static BOOL hb_ntxOrdKeyAdd( LPTAGINFO pTag, PHB_ITEM pItem )
|
|
{
|
|
NTXAREAP pArea = pTag->Owner->Owner;
|
|
BOOL fResult = FALSE;
|
|
LPKEYINFO pKey;
|
|
|
|
if( pArea->lpdbPendingRel )
|
|
SELF_FORCEREL( ( AREAP ) pArea );
|
|
|
|
if( !pArea->fPositioned )
|
|
return FALSE;
|
|
|
|
if( pTag->pForItem && !hb_ntxEvalCond( pArea, pTag->pForItem, TRUE ) )
|
|
return FALSE;
|
|
|
|
if( pTag->Template && pItem && hb_itemType( pItem ) != HB_IT_NIL )
|
|
{
|
|
pKey = hb_ntxKeyPutItem( NULL, pItem, pArea->ulRecNo, pTag, TRUE, NULL );
|
|
}
|
|
else
|
|
{
|
|
pKey = hb_ntxEvalKey( NULL, pTag );
|
|
}
|
|
|
|
if( hb_ntxTagLockWrite( pTag ) )
|
|
{
|
|
if( hb_ntxTagKeyAdd( pTag, pKey ) )
|
|
{
|
|
fResult = TRUE;
|
|
if( !pTag->Owner->fShared && pTag->keyCount &&
|
|
hb_ntxKeyInScope( pTag, pKey ) )
|
|
pTag->keyCount++;
|
|
}
|
|
hb_ntxTagUnLockWrite( pTag );
|
|
}
|
|
hb_ntxKeyFree( pKey );
|
|
return fResult;
|
|
}
|
|
|
|
/*
|
|
* del key from custom tag (ordKeyDel())
|
|
* user key value is not implemented
|
|
*/
|
|
static BOOL hb_ntxOrdKeyDel( LPTAGINFO pTag, PHB_ITEM pItem )
|
|
{
|
|
NTXAREAP pArea = pTag->Owner->Owner;
|
|
BOOL fResult = FALSE;
|
|
LPKEYINFO pKey;
|
|
|
|
if( pArea->lpdbPendingRel )
|
|
SELF_FORCEREL( ( AREAP ) pArea );
|
|
|
|
if( !pArea->fPositioned )
|
|
return FALSE;
|
|
|
|
if( pTag->pForItem && !hb_ntxEvalCond( pArea, pTag->pForItem, TRUE ) )
|
|
return FALSE;
|
|
|
|
if( pTag->Template && pItem && hb_itemType( pItem ) != HB_IT_NIL )
|
|
{
|
|
pKey = hb_ntxKeyPutItem( NULL, pItem, pArea->ulRecNo, pTag, TRUE, NULL );
|
|
}
|
|
else
|
|
{
|
|
pKey = hb_ntxEvalKey( NULL, pTag );
|
|
}
|
|
|
|
if( hb_ntxTagLockWrite( pTag ) )
|
|
{
|
|
if( hb_ntxTagKeyDel( pTag, pKey ) )
|
|
{
|
|
fResult = TRUE;
|
|
if( !pTag->Owner->fShared && pTag->keyCount &&
|
|
hb_ntxKeyInScope( pTag, pKey ) )
|
|
pTag->keyCount--;
|
|
}
|
|
hb_ntxTagUnLockWrite( pTag );
|
|
}
|
|
hb_ntxKeyFree( pKey );
|
|
return fResult;
|
|
}
|
|
|
|
/*
|
|
* DBOI_FINDREC find a specific record in the tag - it's useful for
|
|
* custom indexes when the same record can be stored more then once
|
|
* or when the used index key is unknown
|
|
*/
|
|
static BOOL hb_ntxOrdFindRec( LPTAGINFO pTag, ULONG ulRecNo, BOOL fCont )
|
|
{
|
|
NTXAREAP pArea = pTag->Owner->Owner;
|
|
BOOL fFound = FALSE;
|
|
|
|
if( pTag && ulRecNo )
|
|
{
|
|
if( pArea->lpdbPendingRel && pArea->lpdbPendingRel->isScoped )
|
|
SELF_FORCEREL( ( AREAP ) pArea );
|
|
|
|
if( hb_ntxTagLockRead( pTag ) )
|
|
{
|
|
hb_ntxTagRefreshScope( pTag );
|
|
if( fCont )
|
|
{
|
|
if( ! hb_ntxCurKeyRefresh( pTag ) )
|
|
ulRecNo = 0;
|
|
else
|
|
hb_ntxTagSkipNext( pTag );
|
|
}
|
|
else
|
|
{
|
|
hb_ntxTagGoTop( pTag );
|
|
}
|
|
if( ulRecNo )
|
|
{
|
|
while( !pTag->TagEOF )
|
|
{
|
|
if( pTag->CurKeyInfo->Xtra == ulRecNo )
|
|
{
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
hb_ntxTagSkipNext( pTag );
|
|
}
|
|
}
|
|
hb_ntxTagUnLockRead( pTag );
|
|
}
|
|
}
|
|
SELF_GOTO( ( AREAP ) pArea, fFound ? ulRecNo : 0 );
|
|
return fFound;
|
|
}
|
|
|
|
/*
|
|
* evaluate given C function in given scope
|
|
*/
|
|
static ULONG hb_ntxOrdScopeEval( LPTAGINFO pTag,
|
|
HB_EVALSCOPE_FUNC pFunc, void *pParam,
|
|
PHB_ITEM pItemLo, PHB_ITEM pItemHi )
|
|
{
|
|
ULONG ulCount = 0, ulLen = ( ULONG ) pTag->KeyLength;
|
|
PHB_ITEM pItemTop = hb_itemNew( NULL ), pItemBottom = hb_itemNew( NULL );
|
|
|
|
hb_ntxTagGetScope( pTag, 0, pItemTop );
|
|
hb_ntxTagGetScope( pTag, 1, pItemBottom );
|
|
hb_ntxTagSetScope( pTag, 0, pItemLo );
|
|
hb_ntxTagSetScope( pTag, 1, pItemHi );
|
|
|
|
if( hb_ntxTagLockRead( pTag ) )
|
|
{
|
|
hb_ntxTagGoTop( pTag );
|
|
while( !pTag->TagEOF )
|
|
{
|
|
pFunc( pTag->CurKeyInfo->Xtra, (BYTE *) pTag->CurKeyInfo->key, ulLen, pParam );
|
|
ulCount++;
|
|
hb_ntxTagSkipNext( pTag );
|
|
}
|
|
hb_ntxTagUnLockRead( pTag );
|
|
}
|
|
|
|
hb_ntxTagSetScope( pTag, 0, pItemTop );
|
|
hb_ntxTagSetScope( pTag, 1, pItemBottom );
|
|
hb_itemRelease( pItemTop );
|
|
hb_itemRelease( pItemBottom );
|
|
|
|
return ulCount;
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
/* create index: hb_ntxTagCreate() */
|
|
/* ************************************************************************* */
|
|
|
|
static int hb_ntxQuickSortCompare( LPNTXSORTINFO pSort, BYTE * pKey1, BYTE * pKey2 )
|
|
{
|
|
int iLen = pSort->keyLen, i;
|
|
|
|
i = hb_ntxValCompare( pSort->pTag, (char *) pKey1, iLen, (char *) pKey2, iLen, TRUE );
|
|
if( i == 0 )
|
|
{
|
|
if( pSort->pTag->fSortRec )
|
|
i = ( HB_GET_LE_UINT32( pKey1 + iLen ) < HB_GET_LE_UINT32( pKey2 + iLen ) ) ? -1 : 1;
|
|
}
|
|
else if( ! pSort->pTag->AscendKey )
|
|
{
|
|
i = -i;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
static BOOL hb_ntxQSort( LPNTXSORTINFO pSort, BYTE * pSrc, BYTE * pBuf, LONG lKeys )
|
|
{
|
|
if( lKeys > 1 )
|
|
{
|
|
int iLen = pSort->keyLen + 4;
|
|
LONG l1, l2;
|
|
BYTE * pPtr1, * pPtr2, *pDst;
|
|
BOOL f1, f2;
|
|
|
|
l1 = lKeys >> 1;
|
|
l2 = lKeys - l1;
|
|
pPtr1 = &pSrc[ 0 ];
|
|
pPtr2 = &pSrc[ l1 * iLen ];
|
|
|
|
f1 = hb_ntxQSort( pSort, pPtr1, &pBuf[ 0 ], l1 );
|
|
f2 = hb_ntxQSort( pSort, pPtr2, &pBuf[ l1 * iLen ], l2 );
|
|
if( f1 )
|
|
{
|
|
pDst = pBuf;
|
|
}
|
|
else
|
|
{
|
|
pDst = pSrc;
|
|
pPtr1 = &pBuf[ 0 ];
|
|
}
|
|
if( !f2 )
|
|
{
|
|
pPtr2 = &pBuf[ l1 * iLen ];
|
|
}
|
|
while( l1 > 0 && l2 > 0 )
|
|
{
|
|
if( hb_ntxQuickSortCompare( pSort, pPtr1, pPtr2 ) <= 0 )
|
|
{
|
|
memcpy( pDst, pPtr1, iLen );
|
|
pPtr1 += iLen;
|
|
l1--;
|
|
}
|
|
else
|
|
{
|
|
memcpy( pDst, pPtr2, iLen );
|
|
pPtr2 += iLen;
|
|
l2--;
|
|
}
|
|
pDst += iLen;
|
|
}
|
|
if( l1 > 0 )
|
|
{
|
|
memcpy( pDst, pPtr1, iLen * l1 );
|
|
}
|
|
else if( l2 > 0 && f1 == f2 )
|
|
{
|
|
memcpy( pDst, pPtr2, iLen * l2 );
|
|
}
|
|
return !f1;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void hb_ntxSortSortPage( LPNTXSORTINFO pSort )
|
|
{
|
|
ULONG ulSize = pSort->ulKeys * ( pSort->keyLen + 4 );
|
|
if( !hb_ntxQSort( pSort, pSort->pKeyPool, &pSort->pKeyPool[ ulSize ], pSort->ulKeys ) )
|
|
{
|
|
pSort->pStartKey = &pSort->pKeyPool[ ulSize ];
|
|
}
|
|
else
|
|
{
|
|
pSort->pStartKey = pSort->pKeyPool;
|
|
}
|
|
}
|
|
|
|
static void hb_ntxSortBufferFlush( LPNTXSORTINFO pSort )
|
|
{
|
|
ULONG ulSize;
|
|
if( pSort->ulPagesIO )
|
|
{
|
|
LPNTXINDEX pIndex = pSort->pTag->Owner;
|
|
hb_fsSeekLarge( pIndex->DiskFile,
|
|
hb_ntxFileOffset( pIndex, pSort->ulFirstIO ), FS_SET );
|
|
ulSize = pSort->ulPagesIO * NTXBLOCKSIZE;
|
|
if( hb_fsWriteLarge( pIndex->DiskFile, pSort->pBuffIO, ulSize ) != ulSize )
|
|
{
|
|
hb_ntxErrorRT( pIndex->Owner, EG_WRITE, EDBF_WRITE,
|
|
pIndex->IndexName, hb_fsError(), 0 );
|
|
}
|
|
pSort->ulPagesIO = 0;
|
|
pIndex->fFlush = TRUE;
|
|
if( pIndex->fShared )
|
|
pIndex->Changed = TRUE;
|
|
}
|
|
}
|
|
|
|
static void hb_ntxSortStorePage( LPNTXSORTINFO pSort, LPPAGEINFO pPage )
|
|
{
|
|
LPNTXINDEX pIndex = pSort->pTag->Owner;
|
|
if( !pPage->Page )
|
|
{
|
|
pPage->Page = hb_ntxPageAlloc( pIndex );
|
|
if( pSort->ulSizeIO )
|
|
{
|
|
if( pSort->ulPagesIO == pSort->ulSizeIO )
|
|
hb_ntxSortBufferFlush( pSort );
|
|
if( !pSort->ulPagesIO ||
|
|
hb_ntxFileOffset( pIndex, pSort->ulLastIO ) + NTXBLOCKSIZE ==
|
|
hb_ntxFileOffset( pIndex, pPage->Page ) )
|
|
{
|
|
hb_ntxSetKeyCount( pPage, pPage->uiKeys );
|
|
memcpy( pSort->pBuffIO + pSort->ulPagesIO * NTXBLOCKSIZE,
|
|
hb_ntxPageBuffer( pPage ), NTXBLOCKSIZE );
|
|
pSort->ulLastIO = pPage->Page;
|
|
if( !pSort->ulPagesIO++ )
|
|
pSort->ulFirstIO = pPage->Page;
|
|
pPage->Changed = FALSE;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if( !pPage->pPrev )
|
|
hb_ntxPageSave( pIndex, pPage );
|
|
}
|
|
|
|
static void hb_ntxSortAddNodeKey( LPNTXSORTINFO pSort, BYTE *pKeyVal, ULONG ulRec )
|
|
{
|
|
LPPAGEINFO pPage;
|
|
ULONG ulPage = 0;
|
|
int iLevel = 0;
|
|
|
|
do
|
|
{
|
|
pPage = pSort->NodeList[ iLevel ];
|
|
if( pPage == NULL )
|
|
{
|
|
pPage = pSort->NodeList[ iLevel ] = hb_ntxPageNew( pSort->pTag, TRUE );
|
|
break;
|
|
}
|
|
else if( pPage->uiKeys >= pSort->pTag->MaxKeys )
|
|
{
|
|
hb_ntxSetKeyPage( pPage, pPage->uiKeys, ulPage );
|
|
hb_ntxSortStorePage( pSort, pPage );
|
|
ulPage = pPage->Page;
|
|
hb_ntxPageRelease( pSort->pTag, pPage );
|
|
pSort->NodeList[ iLevel++ ] = hb_ntxPageNew( pSort->pTag, TRUE );
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
while( TRUE );
|
|
|
|
memcpy( hb_ntxGetKeyVal( pPage, pPage->uiKeys ), pKeyVal, pSort->pTag->KeyLength );
|
|
hb_ntxSetKeyRec( pPage, pPage->uiKeys, ulRec );
|
|
hb_ntxSetKeyPage( pPage, pPage->uiKeys, ulPage );
|
|
pPage->uiKeys++;
|
|
}
|
|
|
|
static void hb_ntxSortWritePage( LPNTXSORTINFO pSort )
|
|
{
|
|
ULONG ulSize = pSort->ulKeys * ( pSort->keyLen + 4 );
|
|
|
|
hb_ntxSortSortPage( pSort );
|
|
|
|
if( pSort->hTempFile == FS_ERROR )
|
|
{
|
|
BYTE szName[ _POSIX_PATH_MAX + 1 ];
|
|
pSort->hTempFile = hb_fsCreateTemp( NULL, NULL, FC_NORMAL, szName );
|
|
if( pSort->hTempFile == FS_ERROR )
|
|
{
|
|
hb_errInternal( 9999, "hb_ntxSortWritePage: Can't create temporary file.", "", "" );
|
|
}
|
|
pSort->szTempFileName = hb_strdup( ( char * ) szName );
|
|
}
|
|
pSort->pSwapPage[ pSort->ulCurPage ].ulKeys = pSort->ulKeys;
|
|
pSort->pSwapPage[ pSort->ulCurPage ].nOffset = hb_fsSeekLarge( pSort->hTempFile, 0, FS_END );
|
|
if( hb_fsWriteLarge( pSort->hTempFile, pSort->pStartKey, ulSize ) != ulSize )
|
|
{
|
|
hb_errInternal( 9999, "hb_ntxSortWritePage: Write error in temporary file.", "", "" );
|
|
}
|
|
pSort->ulKeys = 0;
|
|
pSort->ulCurPage++;
|
|
}
|
|
|
|
static void hb_ntxSortGetPageKey( LPNTXSORTINFO pSort, ULONG ulPage,
|
|
BYTE ** pKeyVal, ULONG *pulRec )
|
|
{
|
|
int iLen = pSort->keyLen;
|
|
|
|
if( pSort->pSwapPage[ ulPage ].ulKeyBuf == 0 )
|
|
{
|
|
ULONG ulKeys = HB_MIN( pSort->ulPgKeys, pSort->pSwapPage[ ulPage ].ulKeys );
|
|
ULONG ulSize = ulKeys * ( iLen + 4 );
|
|
|
|
if( hb_fsSeekLarge( pSort->hTempFile, pSort->pSwapPage[ ulPage ].nOffset, FS_SET ) != pSort->pSwapPage[ ulPage ].nOffset ||
|
|
hb_fsReadLarge( pSort->hTempFile, pSort->pSwapPage[ ulPage ].pKeyPool, ulSize ) != ulSize )
|
|
{
|
|
hb_errInternal( 9999, "hb_ntxSortGetPageKey: Read error from temporary file.", "", "" );
|
|
}
|
|
pSort->pSwapPage[ ulPage ].nOffset += ulSize;
|
|
pSort->pSwapPage[ ulPage ].ulKeyBuf = ulKeys;
|
|
pSort->pSwapPage[ ulPage ].ulCurKey = 0;
|
|
}
|
|
*pKeyVal = &pSort->pSwapPage[ ulPage ].pKeyPool[ pSort->pSwapPage[ ulPage ].ulCurKey * ( iLen + 4 ) ];
|
|
*pulRec = HB_GET_LE_UINT32( *pKeyVal + iLen );
|
|
}
|
|
|
|
static void hb_ntxSortOrderPages( LPNTXSORTINFO pSort )
|
|
{
|
|
int iLen = pSort->keyLen, i;
|
|
LONG l, r, m;
|
|
ULONG n, ulPage, ulRec;
|
|
BYTE *pKey = NULL, *pTmp;
|
|
|
|
pSort->ulFirst = 0;
|
|
pSort->pSortedPages = ( ULONG * ) hb_xgrab( pSort->ulPages * sizeof( ULONG ) );
|
|
pSort->pSortedPages[ 0 ] = 0;
|
|
|
|
if( pSort->ulTotKeys > 0 )
|
|
{
|
|
for( n = 0; n < pSort->ulPages; n++ )
|
|
{
|
|
hb_ntxSortGetPageKey( pSort, n, &pKey, &ulRec );
|
|
l = 0;
|
|
r = n - 1;
|
|
while( l <= r )
|
|
{
|
|
m = ( l + r ) >> 1;
|
|
ulPage = pSort->pSortedPages[ m ];
|
|
pTmp = &pSort->pSwapPage[ ulPage ].pKeyPool[ pSort->pSwapPage[ ulPage ].ulCurKey * ( iLen + 4 ) ];
|
|
i = hb_ntxValCompare( pSort->pTag, (char *) pKey, iLen, (char *) pTmp, iLen, TRUE );
|
|
if( i == 0 )
|
|
{
|
|
if( pSort->pTag->fSortRec )
|
|
i = ( ulRec < HB_GET_LE_UINT32( &pTmp[ iLen ] ) ) ? -1 : 1;
|
|
}
|
|
else if( ! pSort->pTag->AscendKey )
|
|
i = -i;
|
|
if( i >= 0 )
|
|
l = m + 1;
|
|
else
|
|
r = m - 1;
|
|
}
|
|
for( r = n; r > l; r-- )
|
|
pSort->pSortedPages[ r ] = pSort->pSortedPages[ r - 1 ];
|
|
pSort->pSortedPages[ l ] = n;
|
|
}
|
|
}
|
|
}
|
|
|
|
static BOOL hb_ntxSortKeyGet( LPNTXSORTINFO pSort, BYTE ** pKeyVal, ULONG *pulRec )
|
|
{
|
|
int iLen = pSort->keyLen, i;
|
|
LONG l, r, m;
|
|
ULONG ulPage;
|
|
|
|
ulPage = pSort->pSortedPages[ pSort->ulFirst ];
|
|
|
|
/* check if first page has some keys yet */
|
|
if( pSort->pSwapPage[ ulPage ].ulKeys > 0 )
|
|
{
|
|
BYTE *pKey, *pTmp;
|
|
ULONG ulRec, ulPg;
|
|
|
|
/*
|
|
* last key was taken from this page - we have to resort it.
|
|
* This is done intentionally here to be sure that the key
|
|
* value return by this function will not be overwritten by
|
|
* next keys in page read from temporary file in function
|
|
* hb_ntxSortGetPageKey() - please do not move this part down
|
|
* even it seems to be correct
|
|
*/
|
|
hb_ntxSortGetPageKey( pSort, ulPage, &pKey, &ulRec );
|
|
|
|
l = pSort->ulFirst + 1;
|
|
r = pSort->ulPages - 1;
|
|
while( l <= r )
|
|
{
|
|
m = ( l + r ) >> 1;
|
|
ulPg = pSort->pSortedPages[ m ];
|
|
pTmp = &pSort->pSwapPage[ ulPg ].pKeyPool[ pSort->pSwapPage[ ulPg ].ulCurKey * ( iLen + 4 ) ];
|
|
i = hb_ntxValCompare( pSort->pTag, (char *) pKey, iLen, (char *) pTmp, iLen, TRUE );
|
|
if( i == 0 )
|
|
{
|
|
if( pSort->pTag->fSortRec )
|
|
i = ( ulRec < HB_GET_LE_UINT32( &pTmp[ iLen ] ) ) ? -1 : 1;
|
|
else
|
|
i = ( ulPage < ulPg ) ? -1 : 1;
|
|
}
|
|
else if( ! pSort->pTag->AscendKey )
|
|
i = -i;
|
|
if( i > 0 )
|
|
l = m + 1;
|
|
else
|
|
r = m - 1;
|
|
}
|
|
if( l > ( LONG ) pSort->ulFirst + 1 )
|
|
{
|
|
ulPage = pSort->pSortedPages[ pSort->ulFirst ];
|
|
for( r = pSort->ulFirst + 1; r < l; r++ )
|
|
pSort->pSortedPages[ r - 1 ] = pSort->pSortedPages[ r ];
|
|
pSort->pSortedPages[ l - 1 ] = ulPage;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pSort->ulFirst++;
|
|
}
|
|
if( pSort->ulFirst < pSort->ulPages )
|
|
{
|
|
ulPage = pSort->pSortedPages[ pSort->ulFirst ];
|
|
hb_ntxSortGetPageKey( pSort, ulPage, pKeyVal, pulRec );
|
|
pSort->pSwapPage[ ulPage ].ulCurKey++;
|
|
pSort->pSwapPage[ ulPage ].ulKeys--;
|
|
pSort->pSwapPage[ ulPage ].ulKeyBuf--;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void hb_ntxSortKeyAdd( LPNTXSORTINFO pSort, ULONG ulRec, char * pKeyVal, int iKeyLen )
|
|
{
|
|
int iLen = pSort->keyLen;
|
|
BYTE *pDst;
|
|
|
|
if( pSort->ulKeys >= pSort->ulPgKeys )
|
|
{
|
|
hb_ntxSortWritePage( pSort );
|
|
}
|
|
pDst = &pSort->pKeyPool[ pSort->ulKeys * ( iLen + 4 ) ];
|
|
|
|
if( iLen > iKeyLen )
|
|
{
|
|
memcpy( pDst, pKeyVal, iKeyLen );
|
|
memset( &pDst[ iKeyLen ], ' ', iLen - iKeyLen );
|
|
}
|
|
else
|
|
{
|
|
memcpy( pDst, pKeyVal, iLen );
|
|
}
|
|
HB_PUT_LE_UINT32( &pDst[ iLen ], ulRec );
|
|
pSort->ulKeys++;
|
|
pSort->ulTotKeys++;
|
|
}
|
|
|
|
static LPNTXSORTINFO hb_ntxSortNew( LPTAGINFO pTag, ULONG ulRecCount )
|
|
{
|
|
LPNTXSORTINFO pSort;
|
|
BYTE * pBuf;
|
|
int iLen = pTag->KeyLength;
|
|
ULONG ulSize, ulMax, ulMin;
|
|
|
|
if( ulRecCount == 0 )
|
|
ulRecCount = 1;
|
|
|
|
pSort = ( LPNTXSORTINFO ) hb_xgrab( sizeof( NTXSORTINFO ) );
|
|
memset( pSort, 0, sizeof( NTXSORTINFO ) );
|
|
|
|
ulMin = ( ULONG ) ceil( sqrt( ( double ) ulRecCount ) );
|
|
ulMax = ( ( ULONG ) ceil( sqrt( ( double ) ulRecCount / ( iLen + 4 ) ) ) ) << 7;
|
|
/*
|
|
* this effectively increase allocated memory buffer for very large files
|
|
* moving the maximum to: 270'566'400 for 4'294'967'295 records and 256
|
|
* index key length.
|
|
* if you want to force smaller buffer I wrote below then add here:
|
|
* ulMax = ulMin;
|
|
*/
|
|
ulSize = ( 1L << 20 ) / ( iLen + 4 );
|
|
while( ulMax < ulSize )
|
|
ulMax <<= 1;
|
|
if( ulMax > ulRecCount )
|
|
ulMax = ulRecCount;
|
|
|
|
do
|
|
{
|
|
ulSize = ulMax * ( iLen + 4 );
|
|
pBuf = ( BYTE * ) hb_xalloc( ulSize << 2 );
|
|
if( pBuf )
|
|
{
|
|
hb_xfree( pBuf );
|
|
pBuf = ( BYTE * ) hb_xalloc( ulSize << 1 );
|
|
}
|
|
else
|
|
{
|
|
ulMax >>= 1;
|
|
}
|
|
}
|
|
while( ! pBuf && ulMax >= ulMin );
|
|
|
|
if( ! pBuf )
|
|
{
|
|
/* call hb_xgrab() to force out of memory error,
|
|
* though in multi process environment this call may return
|
|
* with success when other process free some memory
|
|
* (also the size of buf is reduced to absolute minimum).
|
|
* Sorry but I'm to lazy to implement indexing with smaller
|
|
* memory though it's possible - just simply I can even create
|
|
* index on-line by key adding like in normal update process.
|
|
* The memory necessary to index file is now ~
|
|
* ~ (keySize+4+sizeof(NTXSWAPPAGE)) * sqrt(ulRecCount) * 2
|
|
* so the maximum is for DBF with 2^32 records and keySize 256 ~
|
|
* ~ 2^17 * 284 ~=~ 37 Mb
|
|
* this is not a problem for current computers and I do not see
|
|
* any way to use DBFs with four billions records and indexes with
|
|
* such long (256 bytes) keys on the old ones - they will be simply
|
|
* to slow. IMHO it's also better to signal out of memory here and
|
|
* force some system upgrades then run process which will have to
|
|
* take many hours, Druzus.
|
|
*/
|
|
ulMax = ulMin;
|
|
pBuf = ( BYTE * ) hb_xgrab( ( ulMax << 1 ) * ( iLen + 4 ) );
|
|
}
|
|
|
|
pSort->pTag = pTag;
|
|
pSort->hTempFile = FS_ERROR;
|
|
pSort->keyLen = iLen;
|
|
pSort->fUnique = pTag->UniqueKey;
|
|
pSort->ulMaxKey = ulMax << 1;
|
|
pSort->ulPgKeys = ulMax;
|
|
pSort->ulMaxRec = ulRecCount;
|
|
pSort->pKeyPool = pBuf;
|
|
pSort->ulPages = ( ulRecCount + pSort->ulPgKeys - 1 ) / pSort->ulPgKeys;
|
|
/* check for overflow on 32 bit machines when number of records is nearly 2^32 */
|
|
if( !pSort->ulPages )
|
|
pSort->ulPages = ulRecCount / pSort->ulPgKeys + 1;
|
|
pSort->pSwapPage = ( LPNTXSWAPPAGE ) hb_xgrab( sizeof( NTXSWAPPAGE ) * pSort->ulPages );
|
|
memset( pSort->pSwapPage, 0, sizeof( NTXSWAPPAGE ) * pSort->ulPages );
|
|
return pSort;
|
|
}
|
|
|
|
static void hb_ntxSortFree( LPNTXSORTINFO pSort, BOOL fFull )
|
|
{
|
|
if( pSort->hTempFile != FS_ERROR )
|
|
{
|
|
hb_fsClose( pSort->hTempFile );
|
|
pSort->hTempFile = FS_ERROR;
|
|
}
|
|
if( pSort->szTempFileName )
|
|
{
|
|
hb_fsDelete( ( BYTE * ) pSort->szTempFileName );
|
|
hb_xfree( pSort->szTempFileName );
|
|
pSort->szTempFileName = NULL;
|
|
}
|
|
if( pSort->pKeyPool )
|
|
{
|
|
hb_xfree( pSort->pKeyPool );
|
|
pSort->pKeyPool = NULL;
|
|
}
|
|
if( pSort->pSwapPage )
|
|
{
|
|
hb_xfree( pSort->pSwapPage );
|
|
pSort->pSwapPage = NULL;
|
|
}
|
|
if( pSort->pBuffIO )
|
|
{
|
|
hb_xfree( pSort->pBuffIO );
|
|
pSort->pBuffIO = NULL;
|
|
}
|
|
if( pSort->pSortedPages )
|
|
{
|
|
hb_xfree( pSort->pSortedPages );
|
|
pSort->pSortedPages = NULL;
|
|
}
|
|
if( fFull )
|
|
{
|
|
hb_xfree( pSort );
|
|
}
|
|
}
|
|
|
|
static void hb_ntxSortOut( LPNTXSORTINFO pSort )
|
|
{
|
|
BOOL fUnique = pSort->fUnique, fBalance, fNext;
|
|
LPTAGINFO pTag = pSort->pTag;
|
|
ULONG ulPage, ulRec, ulKey;
|
|
USHORT uiHalf;
|
|
BYTE * pKeyVal;
|
|
int iLen = pSort->keyLen, iLevel;
|
|
|
|
pSort->ulPages = pSort->ulCurPage + 1;
|
|
pSort->ulPgKeys = pSort->ulMaxKey / pSort->ulPages;
|
|
if( pSort->ulPages > 1 )
|
|
{
|
|
BYTE * pBuf = pSort->pKeyPool;
|
|
hb_ntxSortWritePage( pSort );
|
|
for( ulPage = 0; ulPage < pSort->ulPages; ulPage++ )
|
|
{
|
|
pSort->pSwapPage[ ulPage ].ulKeyBuf = 0;
|
|
pSort->pSwapPage[ ulPage ].ulCurKey = 0;
|
|
pSort->pSwapPage[ ulPage ].pKeyPool = pBuf;
|
|
pBuf += pSort->ulPgKeys * ( pSort->keyLen + 4 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hb_ntxSortSortPage( pSort );
|
|
pSort->pSwapPage[ 0 ].ulKeys = pSort->ulKeys;
|
|
pSort->pSwapPage[ 0 ].ulKeyBuf = pSort->ulKeys;
|
|
pSort->pSwapPage[ 0 ].ulCurKey = 0;
|
|
pSort->pSwapPage[ 0 ].pKeyPool = pSort->pStartKey;
|
|
}
|
|
/* printf("pSort->ulPages=%ld, pSort->ulPgKeys=%ld", pSort->ulPages, pSort->ulPgKeys);fflush(stdout); */
|
|
|
|
hb_ntxSortOrderPages( pSort );
|
|
|
|
for( ulKey = 0; ulKey < pSort->ulTotKeys; ulKey++ )
|
|
{
|
|
if( ! hb_ntxSortKeyGet( pSort, &pKeyVal, &ulRec ) )
|
|
{
|
|
hb_errInternal( 9999, "hb_ntxSortOut: memory structure corrupted.", "", "" );
|
|
}
|
|
if( fUnique )
|
|
{
|
|
if( ulKey != 0 && hb_ntxValCompare( pTag, (char *) pSort->pLastKey, iLen, (char *) pKeyVal, iLen, TRUE ) == 0 )
|
|
{
|
|
continue;
|
|
}
|
|
#ifndef HB_NTX_DEBUG_EXT
|
|
else
|
|
{
|
|
memcpy( pSort->pLastKey, pKeyVal, iLen );
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef HB_NTX_DEBUG_EXT
|
|
if( ulKey != 0 )
|
|
{
|
|
int i = hb_ntxValCompare( pTag, (char *) pSort->pLastKey, iLen, (char *) pKeyVal, iLen, TRUE );
|
|
if( ! pTag->AscendKey )
|
|
i = -i;
|
|
if( i == 0 )
|
|
i = ( pSort->ulLastRec < ulRec ) ? -1 : 1;
|
|
if( i > 0 )
|
|
{
|
|
printf("\r\nulKey=%ld, pKeyVal=[%s][%ld], pKeyLast=[%s][%ld]\r\n",
|
|
ulKey, pKeyVal, ulRec, pSort->pLastKey, pSort->ulLastRec); fflush(stdout);
|
|
hb_errInternal( 9999, "hb_ntxSortOut: sorting fails.", "", "" );
|
|
}
|
|
}
|
|
memcpy( pSort->pLastKey, pKeyVal, iLen );
|
|
pSort->ulLastRec = ulRec;
|
|
#endif
|
|
hb_ntxSortAddNodeKey( pSort, pKeyVal, ulRec );
|
|
}
|
|
|
|
#ifdef HB_NTX_DEBUG
|
|
if( hb_ntxSortKeyGet( pSort, &pKeyVal, &ulRec ) )
|
|
{
|
|
hb_errInternal( 9999, "hb_ntxSortOut: memory structure corrupted(2).", "", "" );
|
|
}
|
|
#endif
|
|
|
|
if( pSort->NodeList[ 0 ] == NULL )
|
|
{
|
|
pSort->NodeList[ 0 ] = hb_ntxPageNew( pTag, TRUE );
|
|
}
|
|
hb_ntxSetKeyPage( pSort->NodeList[ 0 ], pSort->NodeList[ 0 ]->uiKeys, 0 );
|
|
|
|
iLevel = 0;
|
|
fNext = TRUE;
|
|
fBalance = FALSE;
|
|
uiHalf = pTag->MaxKeys >> 1;
|
|
do
|
|
{
|
|
hb_ntxSortStorePage( pSort, pSort->NodeList[ iLevel ] );
|
|
if( iLevel + 1 == NTX_STACKSIZE || pSort->NodeList[ iLevel + 1 ] == NULL )
|
|
{
|
|
pTag->RootBlock = pSort->NodeList[ iLevel ]->Page;
|
|
fNext = FALSE;
|
|
}
|
|
else
|
|
{
|
|
hb_ntxSetKeyPage( pSort->NodeList[ iLevel + 1 ],
|
|
pSort->NodeList[ iLevel + 1 ]->uiKeys,
|
|
pSort->NodeList[ iLevel ]->Page );
|
|
if( pSort->NodeList[ iLevel ]->uiKeys < uiHalf )
|
|
{
|
|
fBalance = TRUE;
|
|
}
|
|
}
|
|
hb_ntxPageRelease( pTag, pSort->NodeList[ iLevel ] );
|
|
iLevel++;
|
|
}
|
|
while( fNext );
|
|
|
|
hb_ntxSortBufferFlush( pSort );
|
|
hb_ntxSortFree( pSort, FALSE );
|
|
|
|
if( fBalance )
|
|
{
|
|
LPPAGEINFO pPage, pFirst, pLast;
|
|
|
|
ulPage = pTag->RootBlock;
|
|
while( ulPage )
|
|
{
|
|
pPage = hb_ntxPageLoad( pTag, ulPage );
|
|
if( ! pPage )
|
|
return;
|
|
ulPage = hb_ntxGetKeyPage( pPage, pPage->uiKeys );
|
|
if( ulPage && pPage->uiKeys )
|
|
{
|
|
pLast = hb_ntxPageLoad( pTag, ulPage );
|
|
if( ! pLast )
|
|
{
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
return;
|
|
}
|
|
if( pLast->uiKeys < uiHalf )
|
|
{
|
|
pFirst = hb_ntxPageLoad( pTag, hb_ntxGetKeyPage( pPage,
|
|
pPage->uiKeys - 1 ) );
|
|
if( ! pFirst )
|
|
{
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
hb_ntxPageRelease( pTag, pLast );
|
|
return;
|
|
}
|
|
hb_ntxBalancePages( pTag, pPage, pPage->uiKeys - 1, pFirst, pLast );
|
|
hb_ntxPageRelease( pTag, pFirst );
|
|
}
|
|
hb_ntxPageRelease( pTag, pLast );
|
|
}
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* create tag in index file
|
|
*/
|
|
static ERRCODE hb_ntxTagCreate( LPTAGINFO pTag, BOOL fReindex )
|
|
{
|
|
LPNTXAREA pArea = pTag->Owner->Owner;
|
|
PHB_ITEM pForItem, pWhileItem = NULL, pEvalItem = NULL, pItem = NULL;
|
|
ULONG ulRecCount, ulRecNo = pArea->ulRecNo;
|
|
LPNTXSORTINFO pSort;
|
|
LONG lStep = 0;
|
|
ERRCODE errCode = SUCCESS;
|
|
|
|
if( pArea->lpdbOrdCondInfo )
|
|
{
|
|
pWhileItem = pArea->lpdbOrdCondInfo->itmCobWhile;
|
|
lStep = pArea->lpdbOrdCondInfo->lStep;
|
|
pEvalItem = pArea->lpdbOrdCondInfo->itmCobEval;
|
|
}
|
|
|
|
if( pTag->Custom )
|
|
{
|
|
ulRecCount = 0;
|
|
}
|
|
else
|
|
{
|
|
errCode = SELF_RECCOUNT( ( AREAP ) pArea, &ulRecCount );
|
|
if( errCode != SUCCESS )
|
|
return errCode;
|
|
}
|
|
pArea->pSort = pSort = hb_ntxSortNew( pTag, ulRecCount );
|
|
pSort->fReindex = fReindex;
|
|
|
|
if( ulRecCount == 0 )
|
|
{
|
|
LPPAGEINFO pPage = hb_ntxPageNew( pTag, FALSE );
|
|
|
|
if( pPage )
|
|
{
|
|
pTag->RootBlock = pPage->Page;
|
|
hb_ntxPageRelease( pTag, pPage );
|
|
}
|
|
else
|
|
{
|
|
errCode = FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LPTAGINFO pSaveTag = pArea->lpCurTag;
|
|
ULONG ulStartRec = 0, ulNextCount = 0;
|
|
BOOL fDirectRead, fUseFilter = FALSE;
|
|
BYTE * pSaveRecBuff = pArea->pRecord;
|
|
char szBuffer[ NTX_MAX_KEY ];
|
|
int iRecBuff = 0, iRecBufSize, iRec;
|
|
#ifndef HB_CDP_SUPPORT_OFF
|
|
/* TODO%: this hack is not thread safe, hb_cdp_page has to be thread specific */
|
|
PHB_CODEPAGE cdpTmp = hb_cdp_page;
|
|
hb_cdp_page = pArea->cdPage;
|
|
#endif
|
|
|
|
pForItem = pTag->pForItem;
|
|
if( pTag->nField )
|
|
pItem = hb_itemNew( NULL );
|
|
|
|
if( !pArea->lpdbOrdCondInfo || pArea->lpdbOrdCondInfo->fAll )
|
|
{
|
|
pArea->lpCurTag = NULL;
|
|
}
|
|
else
|
|
{
|
|
if( pArea->lpdbOrdCondInfo->itmRecID )
|
|
ulStartRec = hb_itemGetNL( pArea->lpdbOrdCondInfo->itmRecID );
|
|
if ( ulStartRec )
|
|
{
|
|
ulNextCount = 1;
|
|
}
|
|
else if( pArea->lpdbOrdCondInfo->fRest || pArea->lpdbOrdCondInfo->lNextCount > 0 )
|
|
{
|
|
if( pArea->lpdbOrdCondInfo->itmStartRecID )
|
|
ulStartRec = hb_itemGetNL( pArea->lpdbOrdCondInfo->itmStartRecID );
|
|
if( !ulStartRec )
|
|
ulStartRec = ulRecNo;
|
|
if( pArea->lpdbOrdCondInfo->lNextCount > 0 )
|
|
ulNextCount = pArea->lpdbOrdCondInfo->lNextCount;
|
|
}
|
|
else if( pArea->lpdbOrdCondInfo->fUseFilter )
|
|
{
|
|
fUseFilter = TRUE;
|
|
}
|
|
else if( !pArea->lpdbOrdCondInfo->fUseCurrent )
|
|
{
|
|
pArea->lpCurTag = NULL;
|
|
}
|
|
}
|
|
|
|
fDirectRead = !hb_set.HB_SET_STRICTREAD && /* !pArea->lpdbRelations && */
|
|
( !pArea->lpdbOrdCondInfo || pArea->lpdbOrdCondInfo->fAll ||
|
|
( pArea->lpCurTag == NULL && !fUseFilter ) );
|
|
|
|
pSort->ulSizeIO = ( 1 << 16 ) / NTXBLOCKSIZE;
|
|
pSort->pBuffIO = (BYTE *) hb_xgrab( pSort->ulSizeIO * NTXBLOCKSIZE );
|
|
iRecBufSize = ( pSort->ulSizeIO * NTXBLOCKSIZE ) / pArea->uiRecordLen;
|
|
|
|
if( ulStartRec == 0 && pArea->lpCurTag == NULL )
|
|
ulStartRec = 1;
|
|
|
|
if( ulStartRec == 0 )
|
|
{
|
|
errCode = SELF_GOTOP( ( AREAP ) pArea );
|
|
}
|
|
else
|
|
{
|
|
errCode = SELF_GOTO( ( AREAP ) pArea, ulStartRec );
|
|
if( fUseFilter && errCode == SUCCESS )
|
|
errCode = SELF_SKIPFILTER( ( AREAP ) pArea, 1 );
|
|
}
|
|
|
|
ulRecNo = pArea->ulRecNo;
|
|
|
|
if( ulNextCount && ulRecCount >= ulRecNo + ulNextCount )
|
|
ulRecCount = ulRecNo + ulNextCount - 1;
|
|
|
|
while( errCode == SUCCESS )
|
|
{
|
|
if( ulRecNo > ulRecCount )
|
|
pArea->fEof = TRUE;
|
|
|
|
if( pArea->fEof )
|
|
break;
|
|
|
|
if( fDirectRead )
|
|
{
|
|
if( iRecBuff == 0 || iRecBuff >= iRecBufSize )
|
|
{
|
|
if( ulRecCount - ulRecNo >= (ULONG) iRecBufSize )
|
|
iRec = iRecBufSize;
|
|
else
|
|
iRec = ulRecCount - ulRecNo + 1;
|
|
if( iRec <= 0 )
|
|
break;
|
|
hb_fsSeekLarge( pArea->hDataFile,
|
|
( HB_FOFFSET ) pArea->uiHeaderLen +
|
|
( HB_FOFFSET ) ( ulRecNo - 1 ) *
|
|
( HB_FOFFSET ) pArea->uiRecordLen, FS_SET );
|
|
hb_fsReadLarge( pArea->hDataFile, pSort->pBuffIO, pArea->uiRecordLen * iRec );
|
|
iRecBuff = 0;
|
|
}
|
|
pArea->pRecord = pSort->pBuffIO + iRecBuff * pArea->uiRecordLen;
|
|
pArea->ulRecNo = ulRecNo;
|
|
if( SELF_GETREC( ( AREAP ) pArea, NULL ) == FAILURE )
|
|
break;
|
|
pArea->fValidBuffer = pArea->fPositioned = TRUE;
|
|
pArea->fDeleted = pArea->pRecord[ 0 ] == '*';
|
|
/* Force relational movement in child WorkAreas */
|
|
if( pArea->lpdbRelations )
|
|
{
|
|
errCode = SELF_SYNCCHILDREN( ( AREAP ) pArea );
|
|
if( errCode != SUCCESS )
|
|
break;
|
|
}
|
|
iRecBuff++;
|
|
}
|
|
|
|
if( pWhileItem && !hb_ntxEvalCond( NULL, pWhileItem, FALSE ) )
|
|
break;
|
|
|
|
if( pForItem == NULL || hb_ntxEvalCond( pArea, pForItem, FALSE ) )
|
|
{
|
|
if( pTag->nField )
|
|
errCode = SELF_GETVALUE( ( AREAP ) pArea, pTag->nField, pItem );
|
|
else
|
|
pItem = hb_vmEvalBlockOrMacro( pTag->pKeyItem );
|
|
|
|
switch( hb_itemType( pItem ) )
|
|
{
|
|
case HB_IT_STRING:
|
|
case HB_IT_STRING | HB_IT_MEMO:
|
|
hb_ntxSortKeyAdd( pSort, pArea->ulRecNo,
|
|
hb_itemGetCPtr( pItem ),
|
|
hb_itemGetCLen( pItem ) );
|
|
break;
|
|
|
|
case HB_IT_INTEGER:
|
|
case HB_IT_LONG:
|
|
case HB_IT_DOUBLE:
|
|
hb_ntxNumToStr( pItem, szBuffer, pTag->KeyLength, pTag->KeyDec );
|
|
hb_ntxSortKeyAdd( pSort, pArea->ulRecNo, szBuffer, pTag->KeyLength );
|
|
break;
|
|
|
|
case HB_IT_DATE:
|
|
hb_itemGetDS( pItem, szBuffer );
|
|
hb_ntxSortKeyAdd( pSort, pArea->ulRecNo, szBuffer, 8 );
|
|
break;
|
|
|
|
case HB_IT_LOGICAL:
|
|
szBuffer[0] = hb_itemGetL( pItem ) ? 'T' : 'F';
|
|
hb_ntxSortKeyAdd( pSort, pArea->ulRecNo, szBuffer, 1 );
|
|
break;
|
|
|
|
default:
|
|
hb_ntxErrorRT( pArea, EG_DATATYPE, EDBF_INVALIDKEY,
|
|
pTag->Owner->IndexName, 0, 0 );
|
|
errCode = FAILURE;
|
|
pTag->Partial = TRUE;
|
|
pEvalItem = NULL;
|
|
ulNextCount = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( ulNextCount > 0 )
|
|
{
|
|
if( --ulNextCount == 0 )
|
|
break;
|
|
}
|
|
|
|
if( pEvalItem )
|
|
{
|
|
if( lStep >= pArea->lpdbOrdCondInfo->lStep )
|
|
{
|
|
lStep = 0;
|
|
if( !hb_ntxEvalCond( pArea, pEvalItem, FALSE ) )
|
|
{
|
|
pTag->Partial = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
++lStep;
|
|
}
|
|
|
|
if( fDirectRead )
|
|
ulRecNo++;
|
|
else if( errCode == SUCCESS )
|
|
{
|
|
errCode = SELF_SKIPRAW( ( AREAP ) pArea, 1 );
|
|
if( fUseFilter && errCode == SUCCESS )
|
|
errCode = SELF_SKIPFILTER( ( AREAP ) pArea, 1 );
|
|
ulRecNo = pArea->ulRecNo;
|
|
}
|
|
}
|
|
|
|
if( fDirectRead )
|
|
{
|
|
pArea->pRecord = pSaveRecBuff;
|
|
pArea->fValidBuffer = FALSE;
|
|
if( errCode == SUCCESS )
|
|
errCode = SELF_GOTO( ( AREAP ) pArea, ulRecNo );
|
|
}
|
|
|
|
if( errCode == SUCCESS )
|
|
hb_ntxSortOut( pSort );
|
|
|
|
if( pTag->nField )
|
|
hb_itemRelease( pItem );
|
|
|
|
pArea->lpCurTag = pSaveTag;
|
|
#ifndef HB_CDP_SUPPORT_OFF
|
|
hb_cdp_page = cdpTmp;
|
|
#endif
|
|
}
|
|
|
|
hb_ntxSortFree( pSort, TRUE );
|
|
pArea->pSort = NULL;
|
|
|
|
return errCode;
|
|
}
|
|
|
|
/*
|
|
* recreate tags in index file
|
|
*/
|
|
static ERRCODE hb_ntxReIndex( LPNTXINDEX pIndex )
|
|
{
|
|
ERRCODE errCode = FAILURE;
|
|
int i;
|
|
|
|
if( hb_ntxIndexLockWrite( pIndex, FALSE ) )
|
|
{
|
|
errCode = SUCCESS;
|
|
hb_ntxIndexTrunc( pIndex );
|
|
|
|
for( i = 0; i < pIndex->iTags; i++ )
|
|
{
|
|
LPTAGINFO pTag = pIndex->lpTags[ i ];
|
|
pTag->HeadBlock = pTag->RootBlock = pTag->keyCount = 0;
|
|
pTag->HdrChanged = TRUE;
|
|
errCode = hb_ntxTagCreate( pTag, TRUE );
|
|
if( errCode != SUCCESS )
|
|
break;
|
|
}
|
|
hb_ntxIndexUnLockWrite( pIndex );
|
|
}
|
|
return errCode;
|
|
}
|
|
|
|
|
|
/* ************************************************************************* */
|
|
|
|
/* Implementation of exported functions */
|
|
|
|
static ERRCODE ntxGoBottom( NTXAREAP pArea )
|
|
{
|
|
ERRCODE retval;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxGoBottom(%p)", pArea));
|
|
|
|
if( SELF_GOCOLD( ( AREAP ) pArea ) == FAILURE )
|
|
return FAILURE;
|
|
|
|
if( !pArea->lpCurTag )
|
|
return SUPER_GOBOTTOM( ( AREAP ) pArea );
|
|
|
|
if( pArea->lpdbPendingRel && pArea->lpdbPendingRel->isScoped )
|
|
SELF_FORCEREL( ( AREAP ) pArea );
|
|
|
|
if( ! hb_ntxTagLockRead( pArea->lpCurTag ) )
|
|
return FAILURE;
|
|
hb_ntxTagRefreshScope( pArea->lpCurTag );
|
|
|
|
hb_ntxTagGoBottom( pArea->lpCurTag );
|
|
|
|
pArea->fTop = FALSE;
|
|
pArea->fBottom = TRUE;
|
|
|
|
if( pArea->lpCurTag->TagEOF )
|
|
retval = SELF_GOTO( ( AREAP ) pArea, 0 );
|
|
else
|
|
{
|
|
retval = SELF_GOTO( ( AREAP ) pArea, pArea->lpCurTag->CurKeyInfo->Xtra );
|
|
if( retval != FAILURE && pArea->fPositioned )
|
|
retval = SELF_SKIPFILTER( ( AREAP ) pArea, -1 );
|
|
}
|
|
hb_ntxTagUnLockRead( pArea->lpCurTag );
|
|
|
|
return retval;
|
|
}
|
|
|
|
static ERRCODE ntxGoTop( NTXAREAP pArea )
|
|
{
|
|
ERRCODE retval;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxGoTop(%p)", pArea));
|
|
|
|
if( SELF_GOCOLD( ( AREAP ) pArea ) == FAILURE )
|
|
return FAILURE;
|
|
|
|
if( !pArea->lpCurTag )
|
|
return SUPER_GOTOP( ( AREAP ) pArea );
|
|
|
|
if( pArea->lpdbPendingRel && pArea->lpdbPendingRel->isScoped )
|
|
SELF_FORCEREL( ( AREAP ) pArea );
|
|
|
|
if( ! hb_ntxTagLockRead( pArea->lpCurTag ) )
|
|
return FAILURE;
|
|
hb_ntxTagRefreshScope( pArea->lpCurTag );
|
|
|
|
hb_ntxTagGoTop( pArea->lpCurTag );
|
|
|
|
pArea->fTop = TRUE;
|
|
pArea->fBottom = FALSE;
|
|
|
|
if( pArea->lpCurTag->TagEOF )
|
|
retval = SELF_GOTO( ( AREAP ) pArea, 0 );
|
|
else
|
|
{
|
|
retval = SELF_GOTO( ( AREAP ) pArea, pArea->lpCurTag->CurKeyInfo->Xtra );
|
|
if( retval != FAILURE && pArea->fPositioned )
|
|
retval = SELF_SKIPFILTER( ( AREAP ) pArea, 1 );
|
|
}
|
|
hb_ntxTagUnLockRead( pArea->lpCurTag );
|
|
|
|
return retval;
|
|
}
|
|
|
|
static ERRCODE ntxSeek( NTXAREAP pArea, BOOL fSoftSeek, PHB_ITEM pItem, BOOL fFindLast )
|
|
{
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxSeek(%p, %d, %p, %d)", pArea, fSoftSeek, pItem, fFindLast));
|
|
|
|
if( SELF_GOCOLD( ( AREAP ) pArea ) == FAILURE )
|
|
return FAILURE;
|
|
|
|
if( ! pArea->lpCurTag )
|
|
{
|
|
hb_ntxErrorRT( pArea, EG_NOORDER, EDBF_NOTINDEXED, NULL, 0, EF_CANDEFAULT );
|
|
return FAILURE;
|
|
}
|
|
else
|
|
{
|
|
LPKEYINFO pKey;
|
|
ERRCODE retval = SUCCESS;
|
|
BOOL fEOF = FALSE, fLast;
|
|
USHORT uiLen;
|
|
ULONG ulRec;
|
|
|
|
if( pArea->lpdbPendingRel && pArea->lpdbPendingRel->isScoped )
|
|
SELF_FORCEREL( ( AREAP ) pArea );
|
|
|
|
pArea->fTop = pArea->fBottom = FALSE;
|
|
pArea->fEof = FALSE;
|
|
|
|
fLast = pArea->lpCurTag->fUsrDescend == pArea->lpCurTag->AscendKey ?
|
|
!fFindLast : fFindLast;
|
|
|
|
pKey = hb_ntxKeyPutItem( NULL, pItem, fLast ? NTX_MAX_REC_NUM :
|
|
NTX_IGNORE_REC_NUM, pArea->lpCurTag, TRUE, &uiLen );
|
|
|
|
if( ! hb_ntxTagLockRead( pArea->lpCurTag ) )
|
|
{
|
|
hb_ntxKeyFree( pKey );
|
|
return FAILURE;
|
|
}
|
|
hb_ntxTagRefreshScope( pArea->lpCurTag );
|
|
|
|
if( hb_ntxTagKeyFind( pArea->lpCurTag, pKey, uiLen ) )
|
|
ulRec = pArea->lpCurTag->CurKeyInfo->Xtra;
|
|
else
|
|
ulRec = 0;
|
|
|
|
if( ( ulRec == 0 && ! fSoftSeek ) || pArea->lpCurTag->TagEOF )
|
|
fEOF = TRUE;
|
|
else
|
|
{
|
|
if( ! hb_ntxInBottomScope( pArea->lpCurTag, pArea->lpCurTag->CurKeyInfo->key ) )
|
|
fEOF = TRUE;
|
|
else if( ! hb_ntxInTopScope( pArea->lpCurTag, pArea->lpCurTag->CurKeyInfo->key ) )
|
|
{
|
|
hb_ntxTagGoTop( pArea->lpCurTag );
|
|
if( pArea->lpCurTag->CurKeyInfo->Xtra == 0 ||
|
|
pArea->lpCurTag->TagEOF )
|
|
fEOF = TRUE;
|
|
}
|
|
}
|
|
hb_ntxTagUnLockRead( pArea->lpCurTag );
|
|
if( !fEOF )
|
|
{
|
|
retval = SELF_GOTO( ( AREAP ) pArea, pArea->lpCurTag->CurKeyInfo->Xtra );
|
|
if( retval != FAILURE && pArea->fPositioned )
|
|
{
|
|
retval = SELF_SKIPFILTER( ( AREAP ) pArea, fFindLast ? -1 : 1 );
|
|
if( retval != FAILURE && ulRec && pArea->fPositioned )
|
|
{
|
|
pArea->fFound = ( ulRec == pArea->ulRecNo ||
|
|
hb_ntxValCompare( pArea->lpCurTag, pKey->key, uiLen,
|
|
pArea->lpCurTag->CurKeyInfo->key,
|
|
pArea->lpCurTag->KeyLength, FALSE ) == 0 );
|
|
if( ! pArea->fFound && ! fSoftSeek )
|
|
fEOF = TRUE;
|
|
}
|
|
}
|
|
}
|
|
if( retval != FAILURE && ( fEOF ||
|
|
!hb_ntxKeyInScope( pArea->lpCurTag, pArea->lpCurTag->CurKeyInfo ) ) )
|
|
{
|
|
retval = SELF_GOTO( ( AREAP ) pArea, 0 );
|
|
}
|
|
if( pArea->fPositioned || pArea->ulRecNo != 1 )
|
|
pArea->fBof = FALSE;
|
|
hb_ntxKeyFree( pKey );
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
static ERRCODE ntxSkipRaw( NTXAREAP pArea, LONG lToSkip )
|
|
{
|
|
ERRCODE retval;
|
|
BOOL fOut = FALSE, fForward;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxSkipRaw(%p, %ld)", pArea, lToSkip));
|
|
|
|
if( SELF_GOCOLD( ( AREAP ) pArea ) == FAILURE )
|
|
return FAILURE;
|
|
|
|
if( ! pArea->lpCurTag || lToSkip == 0 )
|
|
return SUPER_SKIPRAW( ( AREAP ) pArea, lToSkip );
|
|
|
|
if( ! hb_ntxTagLockRead( pArea->lpCurTag ) )
|
|
return FAILURE;
|
|
hb_ntxTagRefreshScope( pArea->lpCurTag );
|
|
|
|
fForward = ( lToSkip > 0 );
|
|
|
|
if( ! hb_ntxCurKeyRefresh( pArea->lpCurTag ) )
|
|
{
|
|
if( fForward || pArea->fPositioned )
|
|
fOut = TRUE;
|
|
else
|
|
{
|
|
hb_ntxTagGoBottom( pArea->lpCurTag );
|
|
fOut = pArea->lpCurTag->TagEOF;
|
|
lToSkip++;
|
|
}
|
|
}
|
|
|
|
if( fForward )
|
|
{
|
|
while( !fOut && !pArea->lpCurTag->TagEOF && lToSkip-- > 0 )
|
|
{
|
|
hb_ntxTagSkipNext( pArea->lpCurTag );
|
|
}
|
|
retval = SELF_GOTO( ( AREAP ) pArea,
|
|
( pArea->lpCurTag->TagEOF || fOut ) ? 0 :
|
|
pArea->lpCurTag->CurKeyInfo->Xtra );
|
|
}
|
|
else /* if( lToSkip < 0 ) */
|
|
{
|
|
while( !fOut && !pArea->lpCurTag->TagBOF && lToSkip++ < 0 )
|
|
{
|
|
hb_ntxTagSkipPrev( pArea->lpCurTag );
|
|
}
|
|
if( fOut || pArea->lpCurTag->TagBOF )
|
|
{
|
|
hb_ntxTagGoTop( pArea->lpCurTag );
|
|
fOut = TRUE;
|
|
}
|
|
retval = SELF_GOTO( ( AREAP ) pArea, pArea->lpCurTag->TagEOF ? 0 :
|
|
pArea->lpCurTag->CurKeyInfo->Xtra );
|
|
pArea->fBof = fOut;
|
|
}
|
|
|
|
hb_ntxTagUnLockRead( pArea->lpCurTag );
|
|
/* Update Bof and Eof flags */
|
|
/*
|
|
if( fForward )
|
|
pArea->fBof = FALSE;
|
|
else
|
|
pArea->fEof = FALSE;
|
|
*/
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* Flush _system_ buffers to disk
|
|
*/
|
|
static ERRCODE ntxFlush( NTXAREAP pArea )
|
|
{
|
|
ERRCODE uiError;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxFlush(%p)", pArea));
|
|
|
|
uiError = SELF_GOCOLD( ( AREAP ) pArea );
|
|
if( uiError == SUCCESS )
|
|
{
|
|
uiError = SUPER_FLUSH( ( AREAP ) pArea );
|
|
|
|
if( hb_set.HB_SET_HARDCOMMIT )
|
|
{
|
|
LPNTXINDEX pIndex = pArea->lpIndexes;
|
|
while( pIndex )
|
|
{
|
|
if( pIndex->fFlush /* && !pIndex->Temporary */ )
|
|
{
|
|
hb_fsCommit( pIndex->DiskFile );
|
|
pIndex->fFlush = FALSE;
|
|
}
|
|
pIndex = pIndex->pNext;
|
|
}
|
|
}
|
|
}
|
|
|
|
return uiError;
|
|
}
|
|
|
|
/*
|
|
* Perform a write of WorkArea memory to the data store.
|
|
*/
|
|
static ERRCODE ntxGoCold( NTXAREAP pArea )
|
|
{
|
|
BOOL fRecordChanged = pArea->fRecordChanged;
|
|
BOOL fAppend = pArea->fAppend;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxGoCold(%p)", pArea));
|
|
|
|
if( SUPER_GOCOLD( ( AREAP ) pArea ) == SUCCESS )
|
|
{
|
|
if( fRecordChanged || pArea->fNtxAppend )
|
|
{
|
|
if( fAppend && pArea->fShared )
|
|
{
|
|
if( pArea->fNtxAppend )
|
|
hb_errInternal( 9999, "ntxGoCold: multiple appending without GOCOLD.", "", "" );
|
|
pArea->fNtxAppend = TRUE;
|
|
}
|
|
else
|
|
{
|
|
LPNTXINDEX pIndex = pArea->lpIndexes;
|
|
LPTAGINFO pTag;
|
|
LPKEYINFO pKey;
|
|
BOOL fAdd, fDel, fLck = FALSE;
|
|
int i;
|
|
|
|
/* The pending relation may move the record pointer so we should
|
|
disable them for KEY/FOR evaluation */
|
|
LPDBRELINFO lpdbPendingRel = pArea->lpdbPendingRel;
|
|
pArea->lpdbPendingRel = NULL;
|
|
|
|
if( pArea->fShared )
|
|
{
|
|
fAppend = pArea->fNtxAppend;
|
|
pArea->fNtxAppend = FALSE;
|
|
}
|
|
|
|
while( pIndex )
|
|
{
|
|
for( i = 0; i < pIndex->iTags; i++ )
|
|
{
|
|
pTag = pIndex->lpTags[ i ];
|
|
if( pIndex->fReadonly || pTag->Custom ||
|
|
( pTag->Owner->Compound && !pTag->HeadBlock ) ||
|
|
( fAppend && pTag->ChgOnly ) )
|
|
continue;
|
|
|
|
pKey = hb_ntxEvalKey( NULL, pTag );
|
|
|
|
fAdd = ( pTag->pForItem == NULL ||
|
|
hb_ntxEvalCond( pArea, pTag->pForItem, TRUE ) );
|
|
if( fAppend )
|
|
{
|
|
fDel = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if( hb_ntxValCompare( pTag, pKey->key, pTag->KeyLength,
|
|
pTag->HotKeyInfo->key, pTag->KeyLength, TRUE ) == 0 )
|
|
{
|
|
if( pTag->HotFor ? fAdd : !fAdd )
|
|
fAdd = fDel = FALSE;
|
|
else
|
|
fDel = !fAdd;
|
|
}
|
|
else
|
|
{
|
|
fDel = pTag->HotFor || pTag->Partial;
|
|
}
|
|
}
|
|
if( fDel || fAdd )
|
|
{
|
|
if( !fLck )
|
|
{
|
|
if( !hb_ntxIndexLockWrite( pIndex, TRUE ) )
|
|
{
|
|
hb_ntxKeyFree( pKey );
|
|
break;
|
|
}
|
|
fLck = TRUE;
|
|
if( ( pTag->Owner->Compound && !pTag->HeadBlock ) ||
|
|
!pTag->RootBlock )
|
|
fAdd = fDel = FALSE;
|
|
}
|
|
if( fDel )
|
|
{
|
|
if( hb_ntxTagKeyDel( pTag, pTag->HotKeyInfo ) )
|
|
{
|
|
if( !pIndex->fShared && pTag->keyCount &&
|
|
hb_ntxKeyInScope( pTag, pTag->HotKeyInfo ) )
|
|
pTag->keyCount--;
|
|
}
|
|
else
|
|
{
|
|
if( pTag->ChgOnly )
|
|
fAdd = FALSE;
|
|
else if( !pTag->Partial && !pTag->UniqueKey )
|
|
hb_ntxErrorRT( pTag->Owner->Owner,
|
|
EG_CORRUPTION, EDBF_CORRUPT,
|
|
pTag->Owner->IndexName, 0, 0 );
|
|
}
|
|
}
|
|
if( fAdd )
|
|
{
|
|
if( hb_ntxTagKeyAdd( pTag, pKey ) )
|
|
{
|
|
if( !pIndex->fShared && pTag->keyCount &&
|
|
hb_ntxKeyInScope( pTag, pKey ) )
|
|
pTag->keyCount++;
|
|
}
|
|
}
|
|
}
|
|
hb_ntxKeyFree( pKey );
|
|
}
|
|
if( fLck )
|
|
{
|
|
hb_ntxIndexUnLockWrite( pIndex );
|
|
fLck = FALSE;
|
|
}
|
|
pIndex = pIndex->pNext;
|
|
}
|
|
|
|
/* Restore disabled pending relation */
|
|
pArea->lpdbPendingRel = lpdbPendingRel;
|
|
}
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
return FAILURE;
|
|
}
|
|
|
|
/*
|
|
* Mark the WorkArea data buffer as hot.
|
|
*/
|
|
static ERRCODE ntxGoHot( NTXAREAP pArea )
|
|
{
|
|
ERRCODE errCode;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxGoHot(%p)", pArea));
|
|
|
|
errCode = SUPER_GOHOT( ( AREAP ) pArea );
|
|
if( errCode == SUCCESS )
|
|
{
|
|
if( !pArea->fNtxAppend )
|
|
{
|
|
LPNTXINDEX pIndex = pArea->lpIndexes;
|
|
LPTAGINFO pTag;
|
|
int i;
|
|
|
|
while( pIndex )
|
|
{
|
|
if( !pIndex->fReadonly )
|
|
{
|
|
for( i = 0; i < pIndex->iTags; i++ )
|
|
{
|
|
pTag = pIndex->lpTags[ i ];
|
|
if( !pTag->Custom )
|
|
{
|
|
pTag->HotKeyInfo = hb_ntxEvalKey( pTag->HotKeyInfo, pTag );
|
|
pTag->HotFor = ( pTag->pForItem == NULL ||
|
|
hb_ntxEvalCond( pArea, pTag->pForItem, TRUE ) );
|
|
}
|
|
}
|
|
}
|
|
pIndex = pIndex->pNext;
|
|
}
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
return errCode;
|
|
}
|
|
|
|
/*
|
|
* Close the table in the WorkArea.
|
|
*/
|
|
static ERRCODE ntxClose( NTXAREAP pArea )
|
|
{
|
|
ERRCODE errCode;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxClose(%p)", pArea));
|
|
|
|
if( SELF_GOCOLD( ( AREAP ) pArea ) == FAILURE )
|
|
return FAILURE;
|
|
|
|
errCode = SUPER_CLOSE( ( AREAP ) pArea );
|
|
|
|
if( errCode == SUCCESS )
|
|
{
|
|
if( pArea->pSort )
|
|
{
|
|
hb_ntxSortFree( pArea->pSort, TRUE );
|
|
pArea->pSort = NULL;
|
|
}
|
|
|
|
SELF_ORDLSTCLEAR( ( AREAP ) pArea );
|
|
|
|
/* close also production indexes if any */
|
|
while( pArea->lpIndexes )
|
|
{
|
|
LPNTXINDEX pIndex = pArea->lpIndexes;
|
|
pArea->lpIndexes = pIndex->pNext;
|
|
hb_ntxIndexFree( pIndex );
|
|
}
|
|
|
|
#ifdef HB_NTX_DEBUG_DISP
|
|
printf("\r\n#reads=%ld, #writes=%ld\r\n", s_rdNO, s_wrNO ); fflush(stdout);
|
|
#endif
|
|
}
|
|
|
|
return errCode;
|
|
}
|
|
|
|
/*
|
|
* Retrieve the size of the WorkArea structure.
|
|
*/
|
|
static ERRCODE ntxStructSize( NTXAREAP pArea, USHORT * uiSize )
|
|
|
|
{
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxStrucSize(%p, %p)", pArea, uiSize));
|
|
HB_SYMBOL_UNUSED( pArea );
|
|
|
|
* uiSize = sizeof( NTXAREA );
|
|
return SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Open a data store in the WorkArea.
|
|
*/
|
|
static ERRCODE ntxOpen( NTXAREAP pArea, LPDBOPENINFO pOpenInfo )
|
|
{
|
|
ERRCODE errCode;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxOpen(%p, %p)", pArea, pOpenInfo));
|
|
|
|
errCode = SUPER_OPEN( ( AREAP ) pArea, pOpenInfo );
|
|
|
|
if( errCode == SUCCESS && s_fStruct &&
|
|
( s_fStrictStruct ? pArea->fHasTags : hb_set.HB_SET_AUTOPEN ) )
|
|
{
|
|
char szFileName[ _POSIX_PATH_MAX + 1 ];
|
|
|
|
hb_ntxCreateFName( pArea, NULL, NULL, szFileName, NULL );
|
|
if ( hb_spFile( ( BYTE * ) szFileName, NULL ) )
|
|
{
|
|
DBORDERINFO pOrderInfo;
|
|
|
|
pOrderInfo.itmResult = hb_itemPutNI( NULL, 0 );
|
|
pOrderInfo.atomBagName = hb_itemPutC( NULL, szFileName );
|
|
pOrderInfo.itmNewVal = NULL;
|
|
pOrderInfo.itmOrder = NULL;
|
|
errCode = SELF_ORDLSTADD( ( AREAP ) pArea, &pOrderInfo );
|
|
if( errCode == SUCCESS )
|
|
{
|
|
pOrderInfo.itmOrder = hb_itemPutNI( NULL, hb_set.HB_SET_AUTORDER );
|
|
errCode = SELF_ORDLSTFOCUS( ( AREAP ) pArea, &pOrderInfo );
|
|
hb_itemRelease( pOrderInfo.itmOrder );
|
|
if( errCode == SUCCESS )
|
|
errCode = SELF_GOTOP( ( AREAP ) pArea );
|
|
}
|
|
hb_itemRelease( pOrderInfo.atomBagName );
|
|
hb_itemRelease( pOrderInfo.itmResult );
|
|
}
|
|
}
|
|
|
|
return errCode;
|
|
}
|
|
|
|
static ERRCODE ntxSysName( NTXAREAP pArea, BYTE * pBuffer )
|
|
{
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxSysName(%p, %p)", pArea, pBuffer));
|
|
HB_SYMBOL_UNUSED( pArea );
|
|
|
|
strncpy( ( char * ) pBuffer, "DBFNTX", 7 /* HARBOUR_MAX_RDD_DRIVERNAME_LENGTH */ );
|
|
return SUCCESS;
|
|
}
|
|
|
|
static ERRCODE ntxPack( NTXAREAP pArea )
|
|
{
|
|
ERRCODE errCode;
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxPack(%p)", pArea ));
|
|
|
|
errCode = SUPER_PACK( ( AREAP ) pArea );
|
|
if( errCode == SUCCESS )
|
|
return SELF_ORDLSTREBUILD( ( AREAP ) pArea );
|
|
|
|
return errCode;
|
|
}
|
|
|
|
static ERRCODE ntxZap( NTXAREAP pArea )
|
|
{
|
|
ERRCODE errCode;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxZap(%p)", pArea ));
|
|
|
|
errCode = SUPER_ZAP( ( AREAP ) pArea );
|
|
if( errCode == SUCCESS )
|
|
return SELF_ORDLSTREBUILD( ( AREAP ) pArea );
|
|
|
|
return errCode;
|
|
}
|
|
|
|
static ERRCODE ntxOrderCreate( NTXAREAP pArea, LPDBORDERCREATEINFO pOrderInfo )
|
|
{
|
|
PHB_ITEM pResult, pKeyExp, pForExp = NULL;
|
|
int iLen, iDec, iTag, i;
|
|
char szFileName[ _POSIX_PATH_MAX + 1 ], szSpFile[ _POSIX_PATH_MAX + 1 ],
|
|
szTagName[ NTX_MAX_TAGNAME + 1 ], * szKey, * szFor = NULL;
|
|
LPNTXINDEX pIndex, * pIndexPtr;
|
|
LPTAGINFO pTag = NULL;
|
|
ERRCODE errCode;
|
|
ULONG ulRecNo;
|
|
BOOL fCompound, fTagName, fBagName, fProd, fLocked = FALSE,
|
|
fAscend = TRUE, fCustom = FALSE, fTemporary = FALSE, fExclusive = FALSE;
|
|
BYTE bType;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxOrderCreate(%p, %p)", pArea, pOrderInfo));
|
|
|
|
errCode = SELF_GOCOLD( ( AREAP ) pArea );
|
|
if( errCode != SUCCESS )
|
|
return errCode;
|
|
|
|
if( pArea->lpdbPendingRel )
|
|
{
|
|
errCode = SELF_FORCEREL( ( AREAP ) pArea );
|
|
if( errCode != SUCCESS )
|
|
return errCode;
|
|
}
|
|
|
|
szKey = hb_itemGetCPtr( pOrderInfo->abExpr );
|
|
/* If we have a codeblock for the expression, use it */
|
|
if( pOrderInfo->itmCobExpr )
|
|
pKeyExp = hb_itemNew( pOrderInfo->itmCobExpr );
|
|
else /* Otherwise, try compiling the key expression string */
|
|
{
|
|
errCode = SELF_COMPILE( ( AREAP ) pArea, ( BYTE * ) szKey );
|
|
if( errCode != SUCCESS )
|
|
return errCode;
|
|
pKeyExp = pArea->valResult;
|
|
pArea->valResult = NULL;
|
|
}
|
|
|
|
/* Get a blank record before testing expression */
|
|
ulRecNo = pArea->ulRecNo;
|
|
errCode = SELF_GOTO( ( AREAP ) pArea, 0 );
|
|
if( errCode != SUCCESS )
|
|
return errCode;
|
|
|
|
errCode = SELF_EVALBLOCK( ( AREAP ) pArea, pKeyExp );
|
|
if( errCode != SUCCESS )
|
|
{
|
|
hb_vmDestroyBlockOrMacro( pKeyExp );
|
|
SELF_GOTO( ( AREAP ) pArea, ulRecNo );
|
|
return errCode;
|
|
}
|
|
pResult = pArea->valResult;
|
|
pArea->valResult = NULL;
|
|
|
|
bType = hb_ntxItemType( pResult );
|
|
iLen = iDec = 0;
|
|
switch( bType )
|
|
{
|
|
case 'N':
|
|
hb_itemGetNLen( pResult, &iLen, &iDec );
|
|
if( iDec )
|
|
iLen += iDec + 1;
|
|
break;
|
|
case 'D':
|
|
iLen = 8;
|
|
break;
|
|
case 'L':
|
|
iLen = 1;
|
|
break;
|
|
case 'C':
|
|
iLen = hb_itemGetCLen( pResult );
|
|
if( iLen > NTX_MAX_KEY )
|
|
iLen = NTX_MAX_KEY;
|
|
break;
|
|
default:
|
|
bType = 'U';
|
|
}
|
|
hb_itemRelease( pResult );
|
|
|
|
/* Make sure KEY has proper type and iLen is not 0 */
|
|
if( bType == 'U' || iLen == 0 )
|
|
{
|
|
hb_vmDestroyBlockOrMacro( pKeyExp );
|
|
SELF_GOTO( ( AREAP ) pArea, ulRecNo );
|
|
hb_ntxErrorRT( pArea, bType == 'U' ? EG_DATATYPE : EG_DATAWIDTH,
|
|
1026, NULL, 0, 0 );
|
|
return FAILURE;
|
|
}
|
|
|
|
if( pArea->lpdbOrdCondInfo )
|
|
{
|
|
fAscend = !pArea->lpdbOrdCondInfo->fDescending;
|
|
fCustom = pArea->lpdbOrdCondInfo->fCustom;
|
|
fTemporary = pArea->lpdbOrdCondInfo->fTemporary;
|
|
fExclusive = pArea->lpdbOrdCondInfo->fExclusive;
|
|
/* Check conditional expression */
|
|
szFor = ( char * ) pArea->lpdbOrdCondInfo->abFor;
|
|
if( pArea->lpdbOrdCondInfo->itmCobFor )
|
|
/* If we have a codeblock for the conditional expression, use it */
|
|
pForExp = hb_itemNew( pArea->lpdbOrdCondInfo->itmCobFor );
|
|
else if( szFor )
|
|
{
|
|
/* Otherwise, try compiling the conditional expression string */
|
|
errCode = SELF_COMPILE( ( AREAP ) pArea, ( BYTE * ) szFor );
|
|
if( errCode != SUCCESS )
|
|
{
|
|
hb_vmDestroyBlockOrMacro( pKeyExp );
|
|
SELF_GOTO( ( AREAP ) pArea, ulRecNo );
|
|
return errCode;
|
|
}
|
|
pForExp = pArea->valResult;
|
|
pArea->valResult = NULL;
|
|
}
|
|
}
|
|
|
|
/* Test conditional expression */
|
|
if( pForExp )
|
|
{
|
|
BOOL fOK;
|
|
|
|
errCode = SELF_EVALBLOCK( ( AREAP ) pArea, pForExp );
|
|
if( errCode != SUCCESS )
|
|
{
|
|
hb_vmDestroyBlockOrMacro( pKeyExp );
|
|
hb_vmDestroyBlockOrMacro( pForExp );
|
|
SELF_GOTO( ( AREAP ) pArea, ulRecNo );
|
|
return errCode;
|
|
}
|
|
fOK = hb_itemType( pArea->valResult ) == HB_IT_LOGICAL;
|
|
hb_itemRelease( pArea->valResult );
|
|
pArea->valResult = NULL;
|
|
if( ! fOK )
|
|
{
|
|
hb_vmDestroyBlockOrMacro( pKeyExp );
|
|
hb_vmDestroyBlockOrMacro( pForExp );
|
|
SELF_GOTO( ( AREAP ) pArea, ulRecNo );
|
|
hb_ntxErrorRT( pArea, EG_DATATYPE, EDBF_INVALIDFOR, NULL, 0, 0 );
|
|
return FAILURE;
|
|
}
|
|
}
|
|
|
|
SELF_GOTO( ( AREAP ) pArea, ulRecNo );
|
|
|
|
/*
|
|
* abBagName -> cBag, atomBagName -> cTag
|
|
* The following scheme implemented:
|
|
* 1. abBagName == NULL -> add the Tag to the structural index
|
|
* if no compound index support then create new separate index
|
|
* with atomBagName
|
|
* 2. atomBagName == NULL -> overwrite any index file of abBagName
|
|
* 3. ads the Tag to index file
|
|
*/
|
|
fTagName = pOrderInfo->atomBagName && pOrderInfo->atomBagName[0];
|
|
fBagName = pOrderInfo->abBagName && pOrderInfo->abBagName[0];
|
|
#if defined( HB_NTX_NOMULTITAG )
|
|
fCompound = FALSE;
|
|
#else
|
|
fCompound = fTagName && s_fMultiTag;
|
|
#endif
|
|
hb_ntxCreateFName( pArea, ( char * ) ( ( fBagName || fCompound ) ?
|
|
pOrderInfo->abBagName : pOrderInfo->atomBagName ),
|
|
&fProd, szFileName, szTagName );
|
|
if( fTagName )
|
|
hb_strncpyUpperTrim( szTagName, ( char * ) pOrderInfo->atomBagName, NTX_MAX_TAGNAME );
|
|
|
|
pIndex = hb_ntxFindBag( pArea, szFileName );
|
|
if( pIndex && !fCompound )
|
|
{
|
|
pIndexPtr = &pArea->lpIndexes;
|
|
while( *pIndexPtr )
|
|
{
|
|
if( pIndex == *pIndexPtr )
|
|
{
|
|
*pIndexPtr = pIndex->pNext;
|
|
hb_ntxIndexFree( pIndex );
|
|
break;
|
|
}
|
|
pIndexPtr = &(*pIndexPtr)->pNext;
|
|
}
|
|
pIndex = NULL;
|
|
}
|
|
|
|
if( pIndex )
|
|
{
|
|
if( pIndex->fReadonly )
|
|
{
|
|
hb_vmDestroyBlockOrMacro( pKeyExp );
|
|
if( pForExp != NULL )
|
|
hb_vmDestroyBlockOrMacro( pForExp );
|
|
hb_ntxErrorRT( pArea, EG_READONLY, EDBF_READONLY, pIndex->IndexName, 0, 0 );
|
|
return FAILURE;
|
|
}
|
|
#if 0 /* enable this code if you want to forbid tag deleting in shared mode */
|
|
else if( pIndex->fShared )
|
|
{
|
|
hb_vmDestroyBlockOrMacro( pKeyExp );
|
|
if( pForExp != NULL )
|
|
hb_vmDestroyBlockOrMacro( pForExp );
|
|
hb_ntxErrorRT( pArea, EG_SHARED, EDBF_SHARED, pIndex->IndexName, 0, 0 );
|
|
return FAILURE;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
FHANDLE hFile;
|
|
BOOL bRetry, fOld, fShared = pArea->fShared && !fTemporary && !fExclusive;
|
|
USHORT uiFlags = FO_READWRITE | ( fShared ? FO_DENYNONE : FO_EXCLUSIVE );
|
|
|
|
fOld = fCompound;
|
|
do
|
|
{
|
|
if( fTemporary )
|
|
{
|
|
hFile = hb_fsCreateTemp( NULL, NULL, FC_NORMAL, ( BYTE * ) szSpFile );
|
|
fOld = FALSE;
|
|
}
|
|
else
|
|
{
|
|
hFile = hb_fsExtOpen( ( BYTE * ) szFileName, NULL, uiFlags |
|
|
( fOld ? FXO_APPEND : FXO_TRUNCATE ) |
|
|
FXO_DEFAULTS | FXO_SHARELOCK | FXO_COPYNAME,
|
|
NULL, NULL );
|
|
}
|
|
if( hFile == FS_ERROR )
|
|
bRetry = ( hb_ntxErrorRT( pArea, EG_CREATE, EDBF_CREATE, szFileName,
|
|
hb_fsError(), EF_CANRETRY | EF_CANDEFAULT ) == E_RETRY );
|
|
else
|
|
{
|
|
bRetry = FALSE;
|
|
if( fOld )
|
|
fOld = ( hb_fsSeekLarge( hFile, 0, FS_END ) != 0 );
|
|
}
|
|
} while( bRetry );
|
|
|
|
if( hFile == FS_ERROR )
|
|
{
|
|
hb_vmDestroyBlockOrMacro( pKeyExp );
|
|
if( pForExp != NULL )
|
|
hb_vmDestroyBlockOrMacro( pForExp );
|
|
/* hb_ntxSetTagNumbers() */
|
|
return FAILURE;
|
|
}
|
|
|
|
pIndex = hb_ntxIndexNew( pArea );
|
|
pIndex->IndexName = hb_strdup( szFileName );
|
|
pIndex->fReadonly = FALSE;
|
|
pIndex->fShared = fShared;
|
|
pIndex->DiskFile = hFile;
|
|
pIndex->fDelete = fTemporary;
|
|
if( fTemporary )
|
|
pIndex->RealName = hb_strdup( szSpFile );
|
|
else
|
|
pIndex->Production = fProd;
|
|
|
|
pIndexPtr = &pArea->lpIndexes;
|
|
while( *pIndexPtr )
|
|
pIndexPtr = &(*pIndexPtr)->pNext;
|
|
*pIndexPtr = pIndex;
|
|
if( fOld )
|
|
{
|
|
if( !hb_ntxIndexLockWrite( pIndex, TRUE ) )
|
|
errCode = FAILURE;
|
|
else
|
|
{
|
|
errCode = hb_ntxIndexLoad( pIndex, szTagName );
|
|
if( errCode != SUCCESS )
|
|
hb_ntxIndexUnLockWrite( pIndex );
|
|
else
|
|
fLocked = TRUE;
|
|
}
|
|
if( errCode != SUCCESS )
|
|
{
|
|
*pIndexPtr = pIndex->pNext;
|
|
hb_ntxIndexFree( pIndex );
|
|
hb_vmDestroyBlockOrMacro( pKeyExp );
|
|
if( pForExp != NULL )
|
|
hb_vmDestroyBlockOrMacro( pForExp );
|
|
/* hb_ntxSetTagNumbers() */
|
|
hb_ntxErrorRT( pArea, EG_CORRUPTION, EDBF_CORRUPT, szFileName, 0, 0 );
|
|
return errCode;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pIndex->LargeFile = ( pIndex->Owner->bLockType == DB_DBFLOCK_XHB64 );
|
|
}
|
|
}
|
|
|
|
iTag = hb_ntxFindTagByName( pIndex, szTagName );
|
|
fCompound = ( pIndex->iTags > ( iTag ? 1 : 0 ) );
|
|
|
|
if( ! iTag && pIndex->iTags == CTX_MAX_TAGS )
|
|
{
|
|
hb_vmDestroyBlockOrMacro( pKeyExp );
|
|
if( pForExp != NULL )
|
|
hb_vmDestroyBlockOrMacro( pForExp );
|
|
/* hb_ntxSetTagNumbers() */
|
|
hb_ntxErrorRT( pArea, EG_LIMIT, EDBF_LIMITEXCEEDED, pIndex->IndexName, 0, 0 );
|
|
return FAILURE;
|
|
}
|
|
|
|
if( !fLocked && !hb_ntxIndexLockWrite( pIndex, fCompound ) )
|
|
{
|
|
errCode = FAILURE;
|
|
}
|
|
else
|
|
{
|
|
if( pIndex->Compound != fCompound )
|
|
{
|
|
pIndex->Compound = fCompound;
|
|
if( fCompound )
|
|
{
|
|
if( !pIndex->HeaderBuff )
|
|
pIndex->HeaderBuff = ( BYTE * ) hb_xgrab( NTXBLOCKSIZE );
|
|
memset( pIndex->HeaderBuff, 0, NTXBLOCKSIZE );
|
|
pIndex->fValidHeader = TRUE;
|
|
}
|
|
for( i = 0; i < pIndex->iTags; i++ )
|
|
{
|
|
pIndex->lpTags[ i ]->HdrChanged = TRUE;
|
|
pIndex->lpTags[ i ]->HeadBlock = 0;
|
|
if( fCompound )
|
|
hb_ntxIndexTagAdd( pIndex, pIndex->lpTags[ i ] );
|
|
}
|
|
}
|
|
pTag = hb_ntxTagNew( pIndex, szTagName, fTagName,
|
|
szKey, pKeyExp, bType, (USHORT) iLen, (USHORT) iDec,
|
|
szFor, pForExp,
|
|
fAscend, pOrderInfo->fUnique, fCustom, s_fSortRecNo );
|
|
pTag->Partial = ( pArea->lpdbOrdCondInfo && !pArea->lpdbOrdCondInfo->fAll );
|
|
|
|
if( ! pIndex->Compound )
|
|
{
|
|
while( pIndex->iTags )
|
|
hb_ntxTagDelete( pIndex->lpTags[ 0 ] );
|
|
hb_ntxIndexTrunc( pIndex );
|
|
iTag = 0;
|
|
}
|
|
|
|
if( iTag )
|
|
{
|
|
pTag->HeadBlock = pIndex->lpTags[ iTag - 1 ]->HeadBlock;
|
|
if( pIndex->lpTags[ iTag - 1 ]->RootBlock &&
|
|
! hb_ntxTagPagesFree( pIndex->lpTags[ iTag - 1 ],
|
|
pIndex->lpTags[ iTag - 1 ]->RootBlock ) )
|
|
{
|
|
errCode = FAILURE;
|
|
}
|
|
else
|
|
{
|
|
hb_ntxTagFree( pIndex->lpTags[ iTag - 1 ] );
|
|
pIndex->lpTags[ iTag - 1 ] = pTag;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hb_ntxTagAdd( pIndex, pTag );
|
|
if( pIndex->Compound )
|
|
hb_ntxIndexTagAdd( pIndex, pTag );
|
|
}
|
|
|
|
if( errCode == SUCCESS )
|
|
{
|
|
pIndex->Update = pIndex->Changed = pTag->HdrChanged = TRUE;
|
|
errCode = hb_ntxTagCreate( pTag, FALSE );
|
|
}
|
|
hb_ntxIndexUnLockWrite( pIndex );
|
|
}
|
|
|
|
pIndexPtr = &pArea->lpIndexes;
|
|
while( *pIndexPtr && *pIndexPtr != pIndex )
|
|
pIndexPtr = &(*pIndexPtr)->pNext;
|
|
|
|
/* It should not happen, reintrance? */
|
|
if( !*pIndexPtr )
|
|
return FAILURE;
|
|
|
|
if( errCode != SUCCESS )
|
|
{
|
|
*pIndexPtr = pIndex->pNext;
|
|
hb_ntxIndexFree( pIndex );
|
|
/* hb_ntxSetTagNumbers() */
|
|
return errCode;
|
|
}
|
|
|
|
if( !pArea->lpdbOrdCondInfo || !pArea->lpdbOrdCondInfo->fAdditive )
|
|
{
|
|
*pIndexPtr = pIndex->pNext;
|
|
pIndex->pNext = NULL;
|
|
SELF_ORDLSTCLEAR( ( AREAP ) pArea );
|
|
pIndexPtr = &pArea->lpIndexes;
|
|
while( *pIndexPtr )
|
|
pIndexPtr = &(*pIndexPtr)->pNext;
|
|
*pIndexPtr = pIndex;
|
|
}
|
|
if( pIndex->Production && !pArea->fHasTags &&
|
|
s_fStruct && ( s_fStrictStruct || hb_set.HB_SET_AUTOPEN ) )
|
|
{
|
|
pArea->fHasTags = TRUE;
|
|
if( !pArea->fReadonly && ( pArea->dbfHeader.bHasTags & 0x01 ) == 0 )
|
|
SELF_WRITEDBHEADER( ( AREAP ) pArea );
|
|
}
|
|
/* hb_ntxSetTagNumbers() */
|
|
pArea->lpCurTag = pTag;
|
|
SELF_ORDSETCOND( ( AREAP ) pArea, NULL );
|
|
return SELF_GOTOP( ( AREAP ) pArea );
|
|
}
|
|
|
|
static ERRCODE ntxOrderDestroy( NTXAREAP pArea, LPDBORDERINFO pOrderInfo )
|
|
{
|
|
ERRCODE errCode;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxOrderDestroy(%p, %p)", pArea, pOrderInfo));
|
|
|
|
errCode = SELF_GOCOLD( ( AREAP ) pArea );
|
|
if( errCode != SUCCESS )
|
|
return errCode;
|
|
|
|
if( pArea->lpdbPendingRel )
|
|
{
|
|
errCode = SELF_FORCEREL( ( AREAP ) pArea );
|
|
if( errCode != SUCCESS )
|
|
return errCode;
|
|
}
|
|
|
|
if( pOrderInfo->itmOrder )
|
|
{
|
|
LPTAGINFO pTag = hb_ntxFindTag( pArea, pOrderInfo->itmOrder, pOrderInfo->atomBagName );
|
|
|
|
if( pTag )
|
|
{
|
|
LPNTXINDEX pIndex = pTag->Owner, *pIndexPtr;
|
|
|
|
if( pIndex->iTags == 1 )
|
|
{
|
|
BOOL fProd = pIndex->Production;
|
|
pIndexPtr = &pArea->lpIndexes;
|
|
while( *pIndexPtr != pIndex )
|
|
pIndexPtr = &(*pIndexPtr)->pNext;
|
|
*pIndexPtr = pIndex->pNext;
|
|
pIndex->fDelete = TRUE;
|
|
hb_ntxIndexFree( pIndex );
|
|
if( fProd && pArea->fHasTags &&
|
|
s_fStruct && ( s_fStrictStruct || hb_set.HB_SET_AUTOPEN ) )
|
|
{
|
|
pArea->fHasTags = FALSE;
|
|
if( !pArea->fReadonly && ( pArea->dbfHeader.bHasTags & 0x01 ) != 0 )
|
|
SELF_WRITEDBHEADER( ( AREAP ) pArea );
|
|
}
|
|
}
|
|
else if( pIndex->fReadonly )
|
|
{
|
|
hb_ntxErrorRT( pArea, EG_READONLY, EDBF_READONLY, pIndex->IndexName, 0, 0 );
|
|
return FAILURE;
|
|
}
|
|
#if 0 /* enable this code if you want to forbid tag deleting in shared mode */
|
|
else if( pIndex->fShared )
|
|
{
|
|
hb_ntxErrorRT( pArea, EG_SHARED, EDBF_SHARED, pIndex->IndexName, 0, 0 );
|
|
return FAILURE;
|
|
}
|
|
#endif
|
|
else if( !hb_ntxIndexLockWrite( pIndex, TRUE ) )
|
|
{
|
|
return FAILURE;
|
|
}
|
|
else
|
|
{
|
|
errCode = hb_ntxTagSpaceFree( pTag );
|
|
hb_ntxIndexUnLockWrite( pIndex );
|
|
}
|
|
/* hb_ntxSetTagNumbers() */
|
|
}
|
|
}
|
|
|
|
return errCode;
|
|
}
|
|
|
|
static ERRCODE ntxOrderInfo( NTXAREAP pArea, USHORT uiIndex, LPDBORDERINFO pInfo )
|
|
{
|
|
LPTAGINFO pTag;
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxOrderInfo(%p, %hu, %p)", pArea, uiIndex, pInfo));
|
|
|
|
switch( uiIndex )
|
|
{
|
|
case DBOI_BAGEXT:
|
|
hb_itemClear( pInfo->itmResult );
|
|
return SELF_RDDINFO( SELF_RDDNODE( pArea ), RDDI_ORDBAGEXT, 0, pInfo->itmResult );
|
|
case DBOI_EVALSTEP:
|
|
hb_itemPutNL( pInfo->itmResult,
|
|
pArea->lpdbOrdCondInfo ? pArea->lpdbOrdCondInfo->lStep : 0 );
|
|
return SUCCESS;
|
|
case DBOI_KEYSINCLUDED:
|
|
hb_itemPutNL( pInfo->itmResult,
|
|
pArea->pSort ? pArea->pSort->ulTotKeys : 0 );
|
|
return SUCCESS;
|
|
case DBOI_I_TAGNAME:
|
|
hb_itemPutC( pInfo->itmResult,
|
|
pArea->pSort ? pArea->pSort->pTag->TagName : NULL );
|
|
return SUCCESS;
|
|
case DBOI_I_BAGNAME:
|
|
hb_itemPutC( pInfo->itmResult, pArea->pSort ?
|
|
pArea->pSort->pTag->Owner->IndexName : NULL );
|
|
return SUCCESS;
|
|
case DBOI_ISREINDEX:
|
|
hb_itemPutL( pInfo->itmResult,
|
|
pArea->pSort ? pArea->pSort->fReindex : FALSE );
|
|
return SUCCESS;
|
|
case DBOI_LOCKOFFSET:
|
|
case DBOI_HPLOCKING:
|
|
{
|
|
HB_FOFFSET ulPos, ulPool;
|
|
hb_dbfLockIdxGetData( pArea->bLockType, &ulPos, &ulPool );
|
|
if( uiIndex == DBOI_LOCKOFFSET )
|
|
hb_itemPutNInt( pInfo->itmResult, ulPos );
|
|
else
|
|
hb_itemPutL( pInfo->itmResult, ulPool > 0 );
|
|
return SUCCESS;
|
|
}
|
|
case DBOI_ORDERCOUNT:
|
|
{
|
|
int i = 0;
|
|
BOOL fBag = hb_itemGetCLen( pInfo->atomBagName ) > 0;
|
|
LPNTXINDEX pIndex = fBag ?
|
|
hb_ntxFindBag( pArea, hb_itemGetCPtr( pInfo->atomBagName ) ) :
|
|
pArea->lpIndexes;
|
|
while( pIndex )
|
|
{
|
|
i += pIndex->iTags;
|
|
if( fBag )
|
|
break;
|
|
pIndex = pIndex->pNext;
|
|
}
|
|
hb_itemPutNI( pInfo->itmResult, i );
|
|
return SUCCESS;
|
|
}
|
|
case DBOI_BAGCOUNT:
|
|
{
|
|
int i = 0;
|
|
LPNTXINDEX pIndex = pArea->lpIndexes;
|
|
while( pIndex )
|
|
{
|
|
++i;
|
|
pIndex = pIndex->pNext;
|
|
}
|
|
hb_itemPutNI( pInfo->itmResult, i );
|
|
return SUCCESS;
|
|
}
|
|
case DBOI_BAGNUMBER:
|
|
{
|
|
LPNTXINDEX pIndex = pArea->lpIndexes, pIndexSeek = NULL;
|
|
int i = 0;
|
|
|
|
if( hb_itemGetCLen( pInfo->atomBagName ) > 0 )
|
|
pIndexSeek = hb_ntxFindBag( pArea,
|
|
hb_itemGetCPtr( pInfo->atomBagName ) );
|
|
else if( pArea->lpCurTag )
|
|
pIndexSeek = pArea->lpCurTag->Owner;
|
|
|
|
if( pIndexSeek )
|
|
{
|
|
do
|
|
{
|
|
++i;
|
|
if( pIndex == pIndexSeek )
|
|
break;
|
|
pIndex = pIndex->pNext;
|
|
}
|
|
while( pIndex );
|
|
}
|
|
hb_itemPutNI( pInfo->itmResult, pIndex ? i : 0 );
|
|
return SUCCESS;
|
|
}
|
|
case DBOI_BAGORDER:
|
|
{
|
|
LPNTXINDEX pIndex = pArea->lpIndexes, pIndexSeek;
|
|
int i = 0;
|
|
|
|
if( hb_itemGetCLen( pInfo->atomBagName ) > 0 )
|
|
pIndexSeek = hb_ntxFindBag( pArea,
|
|
hb_itemGetCPtr( pInfo->atomBagName ) );
|
|
else
|
|
pIndexSeek = pIndex;
|
|
|
|
if( pIndexSeek )
|
|
{
|
|
++i;
|
|
do
|
|
{
|
|
if( pIndex == pIndexSeek )
|
|
break;
|
|
i += pIndex->iTags;
|
|
pIndex = pIndex->pNext;
|
|
}
|
|
while( pIndex );
|
|
}
|
|
hb_itemPutNI( pInfo->itmResult, pIndex ? i : 0 );
|
|
return SUCCESS;
|
|
}
|
|
}
|
|
|
|
if( SELF_GOCOLD( ( AREAP ) pArea ) == FAILURE )
|
|
return FAILURE;
|
|
|
|
pTag = hb_ntxFindTag( pArea, pInfo->itmOrder, pInfo->atomBagName );
|
|
|
|
if( pTag )
|
|
{
|
|
switch( uiIndex )
|
|
{
|
|
case DBOI_CONDITION:
|
|
hb_itemPutC( pInfo->itmResult, pTag->ForExpr ? pTag->ForExpr : "" );
|
|
if( hb_itemType( pInfo->itmNewVal ) & HB_IT_STRING )
|
|
{
|
|
char * szForExpr = hb_itemGetCPtr( pInfo->itmNewVal );
|
|
if( pTag->ForExpr ?
|
|
strncmp( pTag->ForExpr, szForExpr, NTX_MAX_EXP ) != 0 :
|
|
*szForExpr )
|
|
{
|
|
PHB_ITEM pForItem = NULL;
|
|
BOOL fOK = *szForExpr == 0;
|
|
if( !fOK )
|
|
{
|
|
if( SELF_COMPILE( ( AREAP ) pArea, ( BYTE *) szForExpr ) == SUCCESS )
|
|
{
|
|
pForItem = pArea->valResult;
|
|
pArea->valResult = NULL;
|
|
if( SELF_EVALBLOCK( ( AREAP ) pArea, pForItem ) == SUCCESS )
|
|
{
|
|
fOK = hb_itemType( pArea->valResult ) == HB_IT_LOGICAL;
|
|
hb_itemRelease( pArea->valResult );
|
|
pArea->valResult = NULL;
|
|
}
|
|
}
|
|
}
|
|
if( fOK && hb_ntxTagLockWrite( pTag ) )
|
|
{
|
|
if( pTag->ForExpr )
|
|
hb_xfree( pTag->ForExpr );
|
|
if( pTag->pForItem )
|
|
hb_vmDestroyBlockOrMacro( pTag->pForItem );
|
|
if( pForItem )
|
|
{
|
|
pTag->ForExpr = hb_strndup( szForExpr, NTX_MAX_EXP );
|
|
pTag->pForItem = pForItem;
|
|
pForItem = NULL;
|
|
}
|
|
else
|
|
{
|
|
pTag->ForExpr = NULL;
|
|
pTag->pForItem = NULL;
|
|
}
|
|
pTag->Partial = TRUE;
|
|
pTag->HdrChanged = TRUE;
|
|
pTag->Owner->Update = TRUE;
|
|
hb_ntxTagUnLockWrite( pTag );
|
|
}
|
|
if( pForItem )
|
|
hb_vmDestroyBlockOrMacro( pForItem );
|
|
}
|
|
}
|
|
break;
|
|
case DBOI_EXPRESSION:
|
|
hb_itemPutC( pInfo->itmResult, pTag->KeyExpr );
|
|
break;
|
|
case DBOI_BAGNAME:
|
|
hb_itemPutC( pInfo->itmResult, pTag->Owner->IndexName );
|
|
break;
|
|
case DBOI_NAME:
|
|
hb_itemPutC( pInfo->itmResult, pTag->TagName );
|
|
break;
|
|
case DBOI_NUMBER:
|
|
hb_itemPutNI( pInfo->itmResult, hb_ntxFindTagNum( pArea, pTag ) );
|
|
break;
|
|
case DBOI_FILEHANDLE:
|
|
hb_itemPutNInt( pInfo->itmResult, pTag->Owner->DiskFile );
|
|
break;
|
|
case DBOI_FULLPATH:
|
|
hb_itemPutC( pInfo->itmResult, pTag->Owner->IndexName );
|
|
break;
|
|
case DBOI_KEYCOUNT:
|
|
hb_itemPutNL( pInfo->itmResult, hb_ntxOrdKeyCount( pTag ) );
|
|
break;
|
|
case DBOI_POSITION:
|
|
if( hb_itemType( pInfo->itmNewVal ) & HB_IT_NUMERIC )
|
|
hb_itemPutL( pInfo->itmResult,
|
|
hb_ntxOrdKeyGoto( pTag, hb_itemGetNL( pInfo->itmNewVal ) ) );
|
|
else
|
|
hb_itemPutNL( pInfo->itmResult, hb_ntxOrdKeyNo( pTag ) );
|
|
break;
|
|
case DBOI_RELKEYPOS:
|
|
if( hb_itemType( pInfo->itmNewVal ) & HB_IT_NUMERIC )
|
|
hb_ntxOrdSetRelKeyPos( pTag, hb_itemGetND( pInfo->itmNewVal ) );
|
|
else
|
|
hb_itemPutND( pInfo->itmResult, hb_ntxOrdGetRelKeyPos( pTag ) );
|
|
break;
|
|
case DBOI_ISCOND:
|
|
hb_itemPutL( pInfo->itmResult, pTag->ForExpr != NULL );
|
|
break;
|
|
case DBOI_ISDESC:
|
|
hb_itemPutL( pInfo->itmResult, pTag->fUsrDescend );
|
|
if( hb_itemType( pInfo->itmNewVal ) == HB_IT_LOGICAL )
|
|
pTag->fUsrDescend = hb_itemGetL( pInfo->itmNewVal );
|
|
break;
|
|
case DBOI_UNIQUE:
|
|
hb_itemPutL( pInfo->itmResult, pTag->UniqueKey );
|
|
break;
|
|
case DBOI_CUSTOM:
|
|
if( hb_itemType( pInfo->itmNewVal ) == HB_IT_LOGICAL )
|
|
{
|
|
if( hb_ntxTagLockWrite( pTag ) )
|
|
{
|
|
if( !pTag->Template )
|
|
{
|
|
BOOL fNewVal = hb_itemGetL( pInfo->itmNewVal );
|
|
if( pTag->Custom ? ! fNewVal : fNewVal )
|
|
{
|
|
pTag->Custom = fNewVal;
|
|
pTag->Partial = TRUE;
|
|
pTag->ChgOnly = FALSE;
|
|
pTag->HdrChanged = TRUE;
|
|
}
|
|
}
|
|
hb_ntxTagUnLockWrite( pTag );
|
|
}
|
|
}
|
|
hb_itemPutL( pInfo->itmResult, pTag->Custom );
|
|
break;
|
|
case DBOI_CHGONLY:
|
|
if( hb_itemType( pInfo->itmNewVal ) == HB_IT_LOGICAL )
|
|
{
|
|
if( hb_ntxTagLockWrite( pTag ) )
|
|
{
|
|
if( !pTag->Custom )
|
|
{
|
|
BOOL fNewVal = hb_itemGetL( pInfo->itmNewVal );
|
|
if( pTag->ChgOnly ? ! fNewVal : fNewVal )
|
|
{
|
|
pTag->ChgOnly = fNewVal;
|
|
pTag->Partial = TRUE;
|
|
pTag->HdrChanged = TRUE;
|
|
}
|
|
}
|
|
hb_ntxTagUnLockWrite( pTag );
|
|
}
|
|
}
|
|
hb_itemPutL( pInfo->itmResult, pTag->ChgOnly );
|
|
break;
|
|
case DBOI_TEMPLATE:
|
|
if( hb_itemType( pInfo->itmNewVal ) == HB_IT_LOGICAL &&
|
|
hb_itemGetL( pInfo->itmNewVal ) )
|
|
{
|
|
if( hb_ntxTagLockWrite( pTag ) )
|
|
{
|
|
if( pTag->Custom && !pTag->Template )
|
|
{
|
|
pTag->Template = TRUE;
|
|
pTag->HdrChanged = TRUE;
|
|
}
|
|
hb_ntxTagUnLockWrite( pTag );
|
|
}
|
|
}
|
|
hb_itemPutL( pInfo->itmResult, pTag->Template );
|
|
break;
|
|
case DBOI_MULTIKEY:
|
|
if( hb_itemGetL( pInfo->itmNewVal ) )
|
|
{
|
|
if( hb_ntxTagLockWrite( pTag ) )
|
|
{
|
|
if( pTag->Custom && !pTag->MultiKey )
|
|
{
|
|
pTag->MultiKey = TRUE;
|
|
pTag->HdrChanged = TRUE;
|
|
}
|
|
hb_ntxTagUnLockWrite( pTag );
|
|
}
|
|
}
|
|
hb_itemPutL( pInfo->itmResult, pTag->MultiKey );
|
|
break;
|
|
case DBOI_PARTIAL:
|
|
hb_itemPutL( pInfo->itmResult, pTag->Partial );
|
|
break;
|
|
case DBOI_SCOPETOP:
|
|
if( pInfo->itmResult )
|
|
hb_ntxTagGetScope( pTag, 0, pInfo->itmResult );
|
|
if( pInfo->itmNewVal )
|
|
hb_ntxTagSetScope( pTag, 0, pInfo->itmNewVal );
|
|
break;
|
|
case DBOI_SCOPEBOTTOM:
|
|
if( pInfo->itmResult )
|
|
hb_ntxTagGetScope( pTag, 1, pInfo->itmResult );
|
|
if( pInfo->itmNewVal )
|
|
hb_ntxTagSetScope( pTag, 1, pInfo->itmNewVal );
|
|
break;
|
|
case DBOI_SCOPESET:
|
|
if( pInfo->itmNewVal )
|
|
{
|
|
hb_ntxTagSetScope( pTag, 0, pInfo->itmNewVal );
|
|
hb_ntxTagSetScope( pTag, 1, pInfo->itmNewVal );
|
|
}
|
|
if( pInfo->itmResult )
|
|
hb_itemClear( pInfo->itmResult );
|
|
break;
|
|
case DBOI_SCOPETOPCLEAR:
|
|
if( pInfo->itmResult )
|
|
hb_ntxTagGetScope( pTag, 0, pInfo->itmResult );
|
|
hb_ntxTagClearScope( pTag, 0 );
|
|
break;
|
|
case DBOI_SCOPEBOTTOMCLEAR:
|
|
if( pInfo->itmResult )
|
|
hb_ntxTagGetScope( pTag, 1, pInfo->itmResult );
|
|
hb_ntxTagClearScope( pTag, 1 );
|
|
break;
|
|
case DBOI_SCOPECLEAR:
|
|
hb_ntxTagClearScope( pTag, 0 );
|
|
hb_ntxTagClearScope( pTag, 1 );
|
|
if( pInfo->itmResult )
|
|
hb_itemClear( pInfo->itmResult );
|
|
case DBOI_KEYADD:
|
|
if( pTag->Owner->fReadonly )
|
|
{
|
|
hb_ntxErrorRT( pArea, EG_READONLY, EDBF_READONLY, pTag->Owner->IndexName, 0, 0 );
|
|
return FAILURE;
|
|
}
|
|
if( pTag->Custom )
|
|
{
|
|
hb_itemPutL( pInfo->itmResult,
|
|
hb_ntxOrdKeyAdd( pTag, pInfo->itmNewVal ) );
|
|
}
|
|
else
|
|
{
|
|
hb_ntxErrorRT( pArea, 0, 1052, NULL, 0, 0 );
|
|
return FAILURE;
|
|
}
|
|
break;
|
|
case DBOI_KEYDELETE:
|
|
if( pTag->Owner->fReadonly )
|
|
{
|
|
hb_ntxErrorRT( pArea, EG_READONLY, EDBF_READONLY, pTag->Owner->IndexName, 0, 0 );
|
|
return FAILURE;
|
|
}
|
|
if( pTag->Custom )
|
|
{
|
|
hb_itemPutL( pInfo->itmResult,
|
|
hb_ntxOrdKeyDel( pTag, pInfo->itmNewVal ) );
|
|
}
|
|
else
|
|
{
|
|
hb_ntxErrorRT( pArea, 0, 1052, NULL, 0, 0 );
|
|
return FAILURE;
|
|
}
|
|
break;
|
|
case DBOI_KEYTYPE:
|
|
{
|
|
char szType[2];
|
|
szType[0] = (char) pTag->KeyType;
|
|
szType[1] = 0;
|
|
hb_itemPutC( pInfo->itmResult, szType );
|
|
}
|
|
break;
|
|
case DBOI_KEYSIZE:
|
|
hb_itemPutNI( pInfo->itmResult, pTag->KeyLength );
|
|
break;
|
|
case DBOI_KEYDEC:
|
|
hb_itemPutNI( pInfo->itmResult, pTag->KeyDec );
|
|
break;
|
|
case DBOI_KEYVAL:
|
|
if( hb_ntxTagLockRead( pTag ) )
|
|
{
|
|
if( hb_ntxCurKeyRefresh( pTag ) )
|
|
hb_ntxKeyGetItem( pInfo->itmResult, pTag->CurKeyInfo, pTag, TRUE );
|
|
else
|
|
hb_itemClear( pInfo->itmResult );
|
|
hb_ntxTagUnLockRead( pTag );
|
|
}
|
|
break;
|
|
case DBOI_SKIPUNIQUE:
|
|
hb_itemPutL( pInfo->itmResult, hb_ntxOrdSkipUnique( pTag,
|
|
hb_itemGetNL( pInfo->itmNewVal ) ) );
|
|
break;
|
|
case DBOI_SKIPEVAL:
|
|
case DBOI_SKIPEVALBACK:
|
|
hb_itemPutL( pInfo->itmResult, hb_ntxOrdSkipEval( pTag,
|
|
uiIndex == DBOI_SKIPEVAL, pInfo->itmNewVal ) );
|
|
break;
|
|
case DBOI_SKIPWILD:
|
|
case DBOI_SKIPWILDBACK:
|
|
hb_itemPutL( pInfo->itmResult, hb_ntxOrdSkipWild( pTag,
|
|
uiIndex == DBOI_SKIPWILD, pInfo->itmNewVal ) );
|
|
break;
|
|
#if defined(__XHARBOUR__)
|
|
case DBOI_SKIPREGEX:
|
|
case DBOI_SKIPREGEXBACK:
|
|
hb_itemPutL( pInfo->itmResult, hb_ntxOrdSkipRegEx( pTag,
|
|
uiIndex == DBOI_SKIPREGEX, pInfo->itmNewVal ) );
|
|
break;
|
|
#endif
|
|
case DBOI_FINDREC:
|
|
case DBOI_FINDRECCONT:
|
|
hb_itemPutL( pInfo->itmResult, hb_ntxOrdFindRec( pTag,
|
|
hb_itemGetNL( pInfo->itmNewVal ),
|
|
uiIndex == DBOI_FINDRECCONT ) );
|
|
break;
|
|
case DBOI_SCOPEEVAL:
|
|
if( hb_itemType( pInfo->itmNewVal ) == HB_IT_ARRAY &&
|
|
hb_arrayLen( pInfo->itmNewVal ) == DBRMI_SIZE &&
|
|
hb_arrayGetPtr( pInfo->itmNewVal, DBRMI_FUNCTION ) != NULL )
|
|
{
|
|
hb_itemPutNL( pInfo->itmResult, hb_ntxOrdScopeEval( pTag,
|
|
( HB_EVALSCOPE_FUNC )
|
|
hb_arrayGetPtr( pInfo->itmNewVal, DBRMI_FUNCTION ),
|
|
hb_arrayGetPtr( pInfo->itmNewVal, DBRMI_PARAM ),
|
|
hb_arrayGetItemPtr( pInfo->itmNewVal, DBRMI_LOVAL ),
|
|
hb_arrayGetItemPtr( pInfo->itmNewVal, DBRMI_HIVAL ) ) );
|
|
}
|
|
else
|
|
{
|
|
hb_itemPutNI( pInfo->itmResult, 0 );
|
|
}
|
|
break;
|
|
case DBOI_UPDATECOUNTER:
|
|
/* refresh update counter */
|
|
if( hb_ntxIndexLockRead( pTag->Owner ) )
|
|
hb_ntxIndexUnLockRead( pTag->Owner );
|
|
hb_itemPutNInt( pInfo->itmResult, pTag->Owner->Version );
|
|
break;
|
|
case DBOI_READLOCK:
|
|
if( hb_itemType( pInfo->itmNewVal ) == HB_IT_LOGICAL )
|
|
{
|
|
hb_itemPutL( pInfo->itmResult,
|
|
hb_itemGetL( pInfo->itmNewVal ) ?
|
|
hb_ntxIndexLockRead( pTag->Owner ) :
|
|
hb_ntxIndexUnLockRead( pTag->Owner ) );
|
|
}
|
|
else
|
|
{
|
|
hb_itemPutL( pInfo->itmResult, pTag->Owner->lockRead > 0 );
|
|
}
|
|
break;
|
|
case DBOI_WRITELOCK:
|
|
if( hb_itemType( pInfo->itmNewVal ) == HB_IT_LOGICAL )
|
|
{
|
|
hb_itemPutL( pInfo->itmResult,
|
|
hb_itemGetL( pInfo->itmNewVal ) ?
|
|
hb_ntxIndexLockWrite( pTag->Owner, TRUE ) :
|
|
hb_ntxIndexUnLockWrite( pTag->Owner ) );
|
|
}
|
|
else
|
|
{
|
|
hb_itemPutL( pInfo->itmResult, pTag->Owner->lockWrite > 0 );
|
|
}
|
|
break;
|
|
case DBOI_ISSORTRECNO:
|
|
hb_itemPutL( pInfo->itmResult, pTag->fSortRec );
|
|
break;
|
|
case DBOI_ISMULTITAG:
|
|
#if defined( HB_NTX_NOMULTITAG )
|
|
hb_itemPutL( pInfo->itmResult, FALSE );
|
|
#else
|
|
hb_itemPutL( pInfo->itmResult, pTag->Owner->Compound );
|
|
#endif
|
|
break;
|
|
case DBOI_LARGEFILE:
|
|
hb_itemPutL( pInfo->itmResult, pTag->Owner->LargeFile );
|
|
break;
|
|
case DBOI_SHARED:
|
|
hb_itemPutL( pInfo->itmResult, pTag->Owner->fShared );
|
|
if( hb_itemType( pInfo->itmNewVal ) == HB_IT_LOGICAL )
|
|
pTag->Owner->fShared = hb_itemGetL( pInfo->itmNewVal );
|
|
break;
|
|
case DBOI_ISREADONLY:
|
|
hb_itemPutL( pInfo->itmResult, pTag->Owner->fReadonly );
|
|
break;
|
|
}
|
|
}
|
|
else if( pInfo->itmResult )
|
|
{
|
|
switch( uiIndex )
|
|
{
|
|
case DBOI_KEYCOUNT:
|
|
{
|
|
ULONG ulRecCount = 0;
|
|
SELF_RECCOUNT( ( AREAP ) pArea, &ulRecCount );
|
|
hb_itemPutNInt( pInfo->itmResult, ulRecCount );
|
|
break;
|
|
}
|
|
case DBOI_POSITION:
|
|
if( pInfo->itmNewVal && hb_itemType( pInfo->itmNewVal ) & HB_IT_NUMERIC )
|
|
hb_itemPutL( pInfo->itmResult, SELF_GOTO( ( AREAP ) pArea,
|
|
hb_itemGetNL( pInfo->itmNewVal ) ) == SUCCESS );
|
|
else
|
|
SELF_RECID( ( AREAP ) pArea, pInfo->itmResult );
|
|
break;
|
|
case DBOI_RELKEYPOS:
|
|
if( hb_itemType( pInfo->itmNewVal ) & HB_IT_NUMERIC )
|
|
{
|
|
double dPos = hb_itemGetND( pInfo->itmNewVal );
|
|
LPTAGINFO pSavedTag = pArea->lpCurTag;
|
|
pArea->lpCurTag = NULL;
|
|
if( dPos >= 1.0 )
|
|
{
|
|
SELF_GOBOTTOM( ( AREAP ) pArea );
|
|
}
|
|
else if( dPos <= 0.0 )
|
|
{
|
|
SELF_GOTOP( ( AREAP ) pArea );
|
|
}
|
|
else
|
|
{
|
|
ULONG ulRecCount, ulRecNo;
|
|
SELF_RECCOUNT( ( AREAP ) pArea, &ulRecCount );
|
|
ulRecNo = ( ULONG ) dPos * ulRecCount + 1;
|
|
if( ulRecNo >= ulRecCount )
|
|
ulRecNo = ulRecCount;
|
|
SELF_GOTO( ( AREAP ) pArea, ulRecNo );
|
|
SELF_SKIPFILTER( ( AREAP ) pArea, 1 );
|
|
if( pArea->fEof )
|
|
SELF_GOTOP( ( AREAP ) pArea );
|
|
}
|
|
pArea->lpCurTag = pSavedTag;
|
|
}
|
|
else
|
|
{
|
|
ULONG ulRecNo = 0, ulRecCount = 0;
|
|
double dPos = 0.0;
|
|
/* resolve any pending relations */
|
|
SELF_RECNO( ( AREAP ) pArea, &ulRecNo );
|
|
if( !pArea->fPositioned )
|
|
{
|
|
if( ulRecNo > 1 )
|
|
dPos = 1.0;
|
|
}
|
|
else
|
|
{
|
|
SELF_RECCOUNT( ( AREAP ) pArea, &ulRecCount );
|
|
if( ulRecCount != 0 )
|
|
dPos = ( 0.5 + ulRecNo ) / ulRecCount;
|
|
}
|
|
hb_itemPutND( pInfo->itmResult, dPos );
|
|
}
|
|
break;
|
|
case DBOI_SKIPUNIQUE:
|
|
hb_itemPutL( pInfo->itmResult, SELF_SKIP( ( AREAP ) pArea,
|
|
hb_itemGetNL( pInfo->itmNewVal ) >= 0 ? 1 : -1 ) == SUCCESS );
|
|
break;
|
|
case DBOI_SKIPEVAL:
|
|
case DBOI_SKIPEVALBACK:
|
|
case DBOI_SKIPWILD:
|
|
case DBOI_SKIPWILDBACK:
|
|
case DBOI_SKIPREGEX:
|
|
case DBOI_SKIPREGEXBACK:
|
|
case DBOI_FINDREC:
|
|
case DBOI_FINDRECCONT:
|
|
SELF_GOTO( ( AREAP ) pArea, 0 );
|
|
hb_itemPutL( pInfo->itmResult, FALSE );
|
|
break;
|
|
case DBOI_ISCOND:
|
|
case DBOI_ISDESC:
|
|
case DBOI_UNIQUE:
|
|
case DBOI_CUSTOM:
|
|
case DBOI_KEYADD:
|
|
case DBOI_KEYDELETE:
|
|
|
|
case DBOI_ISSORTRECNO:
|
|
case DBOI_ISMULTITAG:
|
|
case DBOI_LARGEFILE:
|
|
case DBOI_TEMPLATE:
|
|
case DBOI_MULTIKEY:
|
|
case DBOI_PARTIAL:
|
|
case DBOI_CHGONLY:
|
|
case DBOI_SHARED:
|
|
case DBOI_ISREADONLY:
|
|
case DBOI_WRITELOCK:
|
|
case DBOI_READLOCK:
|
|
hb_itemPutL( pInfo->itmResult, FALSE );
|
|
break;
|
|
case DBOI_KEYVAL:
|
|
case DBOI_SCOPETOP:
|
|
case DBOI_SCOPEBOTTOM:
|
|
case DBOI_SCOPESET:
|
|
case DBOI_SCOPETOPCLEAR:
|
|
case DBOI_SCOPEBOTTOMCLEAR:
|
|
case DBOI_SCOPECLEAR:
|
|
hb_itemClear( pInfo->itmResult );
|
|
break;
|
|
case DBOI_KEYSIZE:
|
|
case DBOI_KEYDEC:
|
|
case DBOI_NUMBER:
|
|
case DBOI_ORDERCOUNT:
|
|
case DBOI_SCOPEEVAL:
|
|
case DBOI_UPDATECOUNTER:
|
|
hb_itemPutNI( pInfo->itmResult, 0 );
|
|
break;
|
|
case DBOI_FILEHANDLE:
|
|
hb_itemPutNInt( pInfo->itmResult, FS_ERROR );
|
|
break;
|
|
default:
|
|
hb_itemPutC( pInfo->itmResult, "" );
|
|
}
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
static ERRCODE ntxCountScope( NTXAREAP pArea, void * pPtr, LONG * plRecNo )
|
|
{
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxCountScope(%p, %p, %p)", pArea, pPtr, plRecNo));
|
|
|
|
if ( pPtr == NULL )
|
|
{
|
|
return SUCCESS;
|
|
}
|
|
return SUPER_COUNTSCOPE( ( AREAP ) pArea, pPtr, plRecNo );
|
|
}
|
|
|
|
static ERRCODE ntxOrderListAdd( NTXAREAP pArea, LPDBORDERINFO pOrderInfo )
|
|
{
|
|
USHORT uiFlags;
|
|
FHANDLE hFile;
|
|
char szFileName[ _POSIX_PATH_MAX + 1 ], szTagName[ NTX_MAX_TAGNAME + 1 ];
|
|
LPNTXINDEX pIndex, *pIndexPtr;
|
|
ERRCODE errCode;
|
|
BOOL fRetry, fReadonly, fShared, fProd;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxOrderListAdd(%p, %p)", pArea, pOrderInfo));
|
|
|
|
errCode = SELF_GOCOLD( ( AREAP ) pArea );
|
|
if( errCode != SUCCESS )
|
|
return errCode;
|
|
|
|
if( hb_itemGetCLen( pOrderInfo->atomBagName ) == 0 )
|
|
return FAILURE;
|
|
|
|
hb_ntxCreateFName( pArea, hb_itemGetCPtr( pOrderInfo->atomBagName ),
|
|
&fProd, szFileName, szTagName );
|
|
|
|
/*
|
|
if( ! szTagName[0] )
|
|
return FAILURE;
|
|
*/
|
|
|
|
pIndex = hb_ntxFindBag( pArea, szFileName );
|
|
|
|
if( ! pIndex )
|
|
{
|
|
fReadonly = pArea->fReadonly;
|
|
fShared = pArea->fShared;
|
|
uiFlags = ( fReadonly ? FO_READ : FO_READWRITE ) |
|
|
( fShared ? FO_DENYNONE : FO_EXCLUSIVE );
|
|
do
|
|
{
|
|
fRetry = FALSE;
|
|
hFile = hb_fsExtOpen( ( BYTE * ) szFileName, NULL, uiFlags |
|
|
FXO_DEFAULTS | FXO_SHARELOCK | FXO_COPYNAME,
|
|
NULL, NULL );
|
|
if( hFile == FS_ERROR )
|
|
{
|
|
fRetry = ( hb_ntxErrorRT( pArea, EG_OPEN, EDBF_OPEN_INDEX, szFileName,
|
|
hb_fsError(), EF_CANRETRY | EF_CANDEFAULT ) == E_RETRY );
|
|
}
|
|
}
|
|
while( fRetry );
|
|
|
|
if( hFile == FS_ERROR )
|
|
return FAILURE;
|
|
|
|
pIndex = hb_ntxIndexNew( pArea );
|
|
pIndex->IndexName = hb_strdup( szFileName );
|
|
pIndex->fReadonly = fReadonly;
|
|
pIndex->fShared = fShared;
|
|
pIndex->DiskFile = hFile;
|
|
pIndex->Production = fProd;
|
|
|
|
pIndexPtr = &pArea->lpIndexes;
|
|
while( *pIndexPtr )
|
|
pIndexPtr = &(*pIndexPtr)->pNext;
|
|
*pIndexPtr = pIndex;
|
|
|
|
if( hb_ntxIndexLockRead( pIndex ) )
|
|
{
|
|
errCode = hb_ntxIndexLoad( pIndex, szTagName );
|
|
hb_ntxIndexUnLockRead( pIndex );
|
|
}
|
|
else
|
|
errCode = FAILURE;
|
|
|
|
if( errCode != SUCCESS )
|
|
{
|
|
*pIndexPtr = pIndex->pNext;
|
|
hb_ntxIndexFree( pIndex );
|
|
hb_ntxErrorRT( pArea, EG_CORRUPTION, EDBF_CORRUPT, szFileName, 0, 0 );
|
|
return errCode;
|
|
}
|
|
/* hb_ntxSetTagNumbers() */
|
|
}
|
|
|
|
if( !pArea->lpCurTag && pIndex->iTags )
|
|
{
|
|
pArea->lpCurTag = pIndex->lpTags[0];
|
|
errCode = SELF_GOTOP( ( AREAP ) pArea );
|
|
}
|
|
return errCode;
|
|
}
|
|
|
|
static ERRCODE ntxOrderListClear( NTXAREAP pArea )
|
|
{
|
|
LPNTXINDEX *pIndexPtr, pIndex;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxOrderListClear(%p)", pArea));
|
|
|
|
if( SELF_GOCOLD( ( AREAP ) pArea ) == FAILURE )
|
|
return FAILURE;
|
|
|
|
pArea->lpCurTag = NULL;
|
|
pIndexPtr = &pArea->lpIndexes;
|
|
while( *pIndexPtr )
|
|
{
|
|
pIndex = *pIndexPtr;
|
|
if( s_fStruct && pIndex->Production &&
|
|
( s_fStrictStruct ? pArea->fHasTags : hb_set.HB_SET_AUTOPEN ) )
|
|
{
|
|
pIndexPtr = &pIndex->pNext;
|
|
}
|
|
else
|
|
{
|
|
*pIndexPtr = pIndex->pNext;
|
|
hb_ntxIndexFree( pIndex );
|
|
}
|
|
}
|
|
/* hb_ntxSetTagNumbers() */
|
|
return SUCCESS;
|
|
}
|
|
|
|
static ERRCODE ntxOrderListDelete( NTXAREAP pArea, LPDBORDERINFO pOrderInfo )
|
|
{
|
|
char szTagName[ NTX_MAX_TAGNAME + 1 ];
|
|
char szFileName[ _POSIX_PATH_MAX + 1 ];
|
|
LPNTXINDEX pIndex, * pIndexPtr;
|
|
BOOL fProd;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxOrderListDelete(%p, %p)", pArea, pOrderInfo));
|
|
|
|
if( SELF_GOCOLD( ( AREAP ) pArea ) == FAILURE )
|
|
return FAILURE;
|
|
|
|
hb_ntxCreateFName( pArea, hb_itemGetCPtr( pOrderInfo->atomBagName ), &fProd,
|
|
szFileName, szTagName );
|
|
pIndex = hb_ntxFindBag( pArea, szFileName );
|
|
|
|
if( pIndex )
|
|
{
|
|
pIndexPtr = &pArea->lpIndexes;
|
|
while( *pIndexPtr )
|
|
{
|
|
if( pIndex == *pIndexPtr )
|
|
{
|
|
*pIndexPtr = pIndex->pNext;
|
|
hb_ntxIndexFree( pIndex );
|
|
/* hb_ntxSetTagNumbers() */
|
|
break;
|
|
}
|
|
pIndexPtr = &(*pIndexPtr)->pNext;
|
|
}
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
static ERRCODE ntxOrderListFocus( NTXAREAP pArea, LPDBORDERINFO pOrderInfo )
|
|
{
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxOrderListFocus(%p, %p)", pArea, pOrderInfo));
|
|
|
|
pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult,
|
|
pArea->lpCurTag ? pArea->lpCurTag->TagName : "" );
|
|
|
|
if( pOrderInfo->itmOrder )
|
|
{
|
|
/* TODO: In Clipper tag is not changed when bad name is given */
|
|
pArea->lpCurTag = hb_ntxFindTag( pArea, pOrderInfo->itmOrder,
|
|
pOrderInfo->atomBagName );
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
static ERRCODE ntxOrderListRebuild( NTXAREAP pArea )
|
|
{
|
|
LPTAGINFO pCurrTag;
|
|
LPNTXINDEX pIndex;
|
|
ERRCODE errCode;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxOrderListRebuild(%p)", pArea));
|
|
|
|
errCode = SELF_GOCOLD( ( AREAP ) pArea );
|
|
if( errCode != SUCCESS )
|
|
return errCode;
|
|
|
|
if( pArea->fShared )
|
|
{
|
|
hb_ntxErrorRT( pArea, EG_SHARED, EDBF_SHARED, pArea->szDataFileName, 0, 0 );
|
|
return FAILURE;
|
|
}
|
|
if( pArea->fReadonly )
|
|
{
|
|
hb_ntxErrorRT( pArea, EG_READONLY, EDBF_READONLY, pArea->szDataFileName, 0, 0 );
|
|
return FAILURE;
|
|
}
|
|
|
|
if( pArea->lpdbPendingRel )
|
|
{
|
|
errCode = SELF_FORCEREL( ( AREAP ) pArea );
|
|
if( errCode != SUCCESS )
|
|
return errCode;
|
|
}
|
|
pCurrTag = pArea->lpCurTag;
|
|
pArea->lpCurTag = NULL;
|
|
pIndex = pArea->lpIndexes;
|
|
while( pIndex && errCode == SUCCESS )
|
|
{
|
|
errCode = hb_ntxReIndex( pIndex );
|
|
pIndex = pIndex->pNext;
|
|
}
|
|
if( errCode == SUCCESS )
|
|
{
|
|
pArea->lpCurTag = pCurrTag;
|
|
errCode = SELF_GOTOP( ( AREAP ) pArea );
|
|
}
|
|
return errCode;
|
|
}
|
|
|
|
static ERRCODE ntxRddInfo( LPRDDNODE pRDD, USHORT uiIndex, ULONG ulConnect, PHB_ITEM pItem )
|
|
{
|
|
LPDBFDATA pData;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("ntxRddInfo(%p, %hu, %lu, %p)", pRDD, uiIndex, ulConnect, pItem));
|
|
|
|
pData = ( LPDBFDATA ) pRDD->lpvCargo;
|
|
|
|
switch( uiIndex )
|
|
{
|
|
case RDDI_ORDBAGEXT:
|
|
case RDDI_ORDEREXT:
|
|
case RDDI_ORDSTRUCTEXT:
|
|
{
|
|
char * szNew = hb_itemGetCPtr( pItem );
|
|
|
|
if( szNew[0] == '.' && szNew[1] )
|
|
szNew = hb_strdup( szNew );
|
|
else
|
|
szNew = NULL;
|
|
|
|
hb_itemPutC( pItem, pData->szIndexExt[ 0 ] ? pData->szIndexExt : NTX_INDEXEXT );
|
|
if( szNew )
|
|
{
|
|
hb_strncpy( pData->szIndexExt, szNew, HB_MAX_FILE_EXT );
|
|
hb_xfree( szNew );
|
|
}
|
|
break;
|
|
}
|
|
|
|
case RDDI_MULTITAG:
|
|
{
|
|
#if defined( HB_NTX_NOMULTITAG )
|
|
hb_itemPutL( pItem, FALSE );
|
|
#else
|
|
BOOL fMultiTag = s_fMultiTag;
|
|
if( hb_itemType( pItem ) == HB_IT_LOGICAL )
|
|
s_fMultiTag = hb_itemGetL( pItem );
|
|
hb_itemPutL( pItem, fMultiTag );
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case RDDI_SORTRECNO:
|
|
{
|
|
BOOL fSortRecNo = s_fSortRecNo;
|
|
if( hb_itemType( pItem ) == HB_IT_LOGICAL )
|
|
s_fSortRecNo = hb_itemGetL( pItem );
|
|
hb_itemPutL( pItem, fSortRecNo );
|
|
break;
|
|
}
|
|
|
|
case RDDI_STRUCTORD:
|
|
{
|
|
BOOL fStruct = s_fStruct;
|
|
if( hb_itemType( pItem ) == HB_IT_LOGICAL )
|
|
s_fStruct = hb_itemGetL( pItem );
|
|
hb_itemPutL( pItem, fStruct );
|
|
break;
|
|
}
|
|
|
|
case RDDI_STRICTSTRUCT:
|
|
{
|
|
BOOL fStrictStruct = s_fStrictStruct;
|
|
if( hb_itemType( pItem ) == HB_IT_LOGICAL )
|
|
s_fStrictStruct = hb_itemGetL( pItem );
|
|
hb_itemPutL( pItem, fStrictStruct );
|
|
break;
|
|
}
|
|
|
|
case RDDI_MULTIKEY:
|
|
{
|
|
BOOL fMultiKey = s_fMultiKey;
|
|
if( hb_itemType( pItem ) == HB_IT_LOGICAL )
|
|
s_fMultiKey = hb_itemGetL( pItem );
|
|
hb_itemPutL( pItem, fMultiKey );
|
|
break;
|
|
}
|
|
|
|
|
|
default:
|
|
return SUPER_RDDINFO( pRDD, uiIndex, ulConnect, pItem );
|
|
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
static RDDFUNCS ntxTable = { ntxBof,
|
|
ntxEof,
|
|
ntxFound,
|
|
( DBENTRYP_V ) ntxGoBottom,
|
|
ntxGoTo,
|
|
ntxGoToId,
|
|
( DBENTRYP_V ) ntxGoTop,
|
|
( DBENTRYP_BIB ) ntxSeek,
|
|
ntxSkip,
|
|
ntxSkipFilter,
|
|
( DBENTRYP_L ) ntxSkipRaw,
|
|
ntxAddField,
|
|
( DBENTRYP_B ) ntxAppend,
|
|
ntxCreateFields,
|
|
ntxDeleteRec,
|
|
ntxDeleted,
|
|
ntxFieldCount,
|
|
ntxFieldDisplay,
|
|
ntxFieldInfo,
|
|
ntxFieldName,
|
|
( DBENTRYP_V ) ntxFlush,
|
|
ntxGetRec,
|
|
ntxGetValue,
|
|
ntxGetVarLen,
|
|
( DBENTRYP_V ) ntxGoCold,
|
|
( DBENTRYP_V ) ntxGoHot,
|
|
ntxPutRec,
|
|
ntxPutValue,
|
|
ntxRecall,
|
|
ntxRecCount,
|
|
ntxRecInfo,
|
|
ntxRecNo,
|
|
ntxRecId,
|
|
ntxSetFieldsExtent,
|
|
ntxAlias,
|
|
( DBENTRYP_V ) ntxClose,
|
|
ntxCreate,
|
|
ntxInfo,
|
|
ntxNewArea,
|
|
( DBENTRYP_VP ) ntxOpen,
|
|
ntxRelease,
|
|
( DBENTRYP_SP ) ntxStructSize,
|
|
( DBENTRYP_P ) ntxSysName,
|
|
ntxEval,
|
|
( DBENTRYP_V ) ntxPack,
|
|
ntPackRec,
|
|
ntxSort,
|
|
ntxTrans,
|
|
ntxTransRec,
|
|
( DBENTRYP_V ) ntxZap,
|
|
ntxchildEnd,
|
|
ntxchildStart,
|
|
ntxchildSync,
|
|
ntxsyncChildren,
|
|
ntxclearRel,
|
|
ntxforceRel,
|
|
ntxrelArea,
|
|
ntxrelEval,
|
|
ntxrelText,
|
|
ntxsetRel,
|
|
( DBENTRYP_OI ) ntxOrderListAdd,
|
|
( DBENTRYP_V ) ntxOrderListClear,
|
|
( DBENTRYP_OI ) ntxOrderListDelete,
|
|
( DBENTRYP_OI ) ntxOrderListFocus,
|
|
( DBENTRYP_V ) ntxOrderListRebuild,
|
|
ntxOrderCondition,
|
|
( DBENTRYP_VOC ) ntxOrderCreate,
|
|
( DBENTRYP_OI ) ntxOrderDestroy,
|
|
( DBENTRYP_OII ) ntxOrderInfo,
|
|
ntxClearFilter,
|
|
ntxClearLocate,
|
|
ntxClearScope,
|
|
( DBENTRYP_VPLP ) ntxCountScope,
|
|
ntxFilterText,
|
|
ntxScopeInfo,
|
|
ntxSetFilter,
|
|
ntxSetLocate,
|
|
ntxSetScope,
|
|
ntxSkipScope,
|
|
ntxLocate,
|
|
ntxCompile,
|
|
ntxError,
|
|
ntxEvalBlock,
|
|
ntxRawLock,
|
|
ntxLock,
|
|
ntxUnLock,
|
|
ntxCloseMemFile,
|
|
ntxCreateMemFile,
|
|
ntxGetValueFile,
|
|
ntxOpenMemFile,
|
|
ntxPutValueFile,
|
|
ntxReadDBHeader,
|
|
ntxWriteDBHeader,
|
|
ntxInit,
|
|
ntxExit,
|
|
ntxDrop,
|
|
ntxExists,
|
|
( DBENTRYP_RSLV ) ntxRddInfo,
|
|
ntxWhoCares
|
|
};
|
|
|
|
HB_FUNC( DBFNTX ) {;}
|
|
|
|
HB_FUNC( DBFNTX_GETFUNCTABLE )
|
|
{
|
|
RDDFUNCS * pTable;
|
|
USHORT * uiCount, uiRddId;
|
|
|
|
uiCount = ( USHORT * ) hb_itemGetPtr( hb_param( 1, HB_IT_POINTER ) );
|
|
pTable = ( RDDFUNCS * ) hb_itemGetPtr( hb_param( 2, HB_IT_POINTER ) );
|
|
uiRddId = hb_parni( 4 );
|
|
|
|
if( pTable )
|
|
{
|
|
ERRCODE errCode;
|
|
|
|
if( uiCount )
|
|
* uiCount = RDDFUNCSCOUNT;
|
|
errCode = hb_rddInherit( pTable, &ntxTable, &ntxSuper, ( BYTE * ) "DBFFPT" );
|
|
if( errCode != SUCCESS )
|
|
errCode = hb_rddInherit( pTable, &ntxTable, &ntxSuper, ( BYTE * ) "DBFDBT" );
|
|
if( errCode != SUCCESS )
|
|
errCode = hb_rddInherit( pTable, &ntxTable, &ntxSuper, ( BYTE * ) "DBF" );
|
|
if( errCode == SUCCESS )
|
|
{
|
|
/*
|
|
* we successfully register our RDD so now we can initialize it
|
|
* You may think that this place is RDD init statement, Druzus
|
|
*/
|
|
s_uiRddId = uiRddId;
|
|
}
|
|
hb_retni( errCode );
|
|
}
|
|
else
|
|
{
|
|
hb_retni( FAILURE );
|
|
}
|
|
}
|
|
|
|
|
|
#define __PRG_SOURCE__ __FILE__
|
|
|
|
#ifdef HB_PCODE_VER
|
|
# undef HB_PRG_PCODE_VER
|
|
# define HB_PRG_PCODE_VER HB_PCODE_VER
|
|
#endif
|
|
|
|
HB_FUNC_EXTERN( _DBF );
|
|
|
|
static void hb_dbfntxRddInit( void * cargo )
|
|
{
|
|
HB_SYMBOL_UNUSED( cargo );
|
|
|
|
if( hb_rddRegister( "DBF", RDT_FULL ) <= 1 )
|
|
{
|
|
USHORT usResult;
|
|
|
|
hb_rddRegister( "DBFFPT", RDT_FULL );
|
|
usResult = hb_rddRegister( "DBFNTX", RDT_FULL );
|
|
if( usResult <= 1 )
|
|
{
|
|
if( usResult == 0 )
|
|
{
|
|
PHB_ITEM pItem = hb_itemPutNI( NULL, DB_MEMO_DBT );
|
|
SELF_RDDINFO( hb_rddGetNode( s_uiRddId ), RDDI_MEMOTYPE, 0, pItem );
|
|
hb_itemRelease( pItem );
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
hb_errInternal( HB_EI_RDDINVALID, NULL, NULL, NULL );
|
|
|
|
/* not executed, only to force DBF RDD linking */
|
|
HB_FUNC_EXEC( _DBF );
|
|
}
|
|
|
|
HB_INIT_SYMBOLS_BEGIN( dbfntx1__InitSymbols )
|
|
{ "DBFNTX", {HB_FS_PUBLIC}, {HB_FUNCNAME( DBFNTX )}, NULL },
|
|
{ "DBFNTX_GETFUNCTABLE", {HB_FS_PUBLIC}, {HB_FUNCNAME( DBFNTX_GETFUNCTABLE )}, NULL }
|
|
HB_INIT_SYMBOLS_END( dbfntx1__InitSymbols )
|
|
|
|
HB_CALL_ON_STARTUP_BEGIN( _hb_dbfntx_rdd_init_ )
|
|
hb_vmAtInit( hb_dbfntxRddInit, NULL );
|
|
HB_CALL_ON_STARTUP_END( _hb_dbfntx_rdd_init_ )
|
|
|
|
#if defined(HB_PRAGMA_STARTUP)
|
|
# pragma startup dbfntx1__InitSymbols
|
|
# pragma startup _hb_dbfntx_rdd_init_
|
|
#elif defined(HB_MSC_STARTUP)
|
|
# if _MSC_VER >= 1010
|
|
# pragma data_seg( ".CRT$XIY" )
|
|
# pragma comment( linker, "/Merge:.CRT=.data" )
|
|
# else
|
|
# pragma data_seg( "XIY" )
|
|
# endif
|
|
static HB_$INITSYM hb_vm_auto_dbfntx1__InitSymbols = dbfntx1__InitSymbols;
|
|
static HB_$INITSYM hb_vm_auto_dbfntx_rdd_init = _hb_dbfntx_rdd_init_;
|
|
# pragma data_seg()
|
|
#endif
|