

To:   ALL (Foxpro Developers)
From: Warren Master CIS 70713,2002



The Following are observations I have made when working with Foxpro 2.5
and Index Expressions that invlove UDF's. I hope that the information
here will help anyone working with these types of Keys and save them 
time and fustration in trying to figure out exactly what is happening
inside FOXPRO. I make no clains to the accuracy of this information. The
information is based on my work on a project and this inforation resulted in
the success of that project.


* All the examples below were constructed specfically for demonstrating
* the findings in this document.


The Index UDF that I have constructed has Three modes

1) Used in Index on and Reindex Commands
	The UDF generates The Keys

2) Used for Inserting New Records
   The Key is generated before the UDF is called , in this case the UDf simply returns
   the key which is stored in a Global Variable ahead of time.

3) Used for Replaces
   The udf Returnes two differnt keys genrated ahead of time.
	A toggle is set to Return the Old key the first call and the new 
	Key the second time.


Some points of Interest:

	The Index UDF should be passed a field so that is changed
	So Foxpro Knows to Call it the UDF. 

	ie) if the feild WORD is changed and the Index expression is 
	INDEX on UDF() Foxpro may not call the UDF.
    USe Ondex on UDF(WORD) instead even if you don't need WORD passed in.


	Append Blank/Replace Update the index Twice
		Once to Put a blank Key into the Index (No assumtions are made that a replace will be issuied)
		(if No Key was put in and a replace was not issued no refernce to the Record
		 would be in the Index)

		Second time when replace is called .

		This is why Insert Into is faster becaues the Index is updated Once.
		and the New Record is added and updated in on step.

	Foxpro Updates the Index diffrently when adding new records. This depends
	on the setting of Exclusive for the DBF.
	
	if Exclusive is set on Foxpro will Update the index only when the Record
	pointer is moved of the Record not immediately after the Insert.

	If Exclusive is Set Off The Index is Updated Imedialty during the Insert.

	Replace seems to Update the Index regardless of the Exclusive setting. It
	Updates the Index right away.

	Replace is a little differnt. It requires Two Keys
	Replace Calles the Index Udf Twice for Each Replace.
	The first time it wants the Old Key value of the Data that is going to
	be replaced (it probably needs this to remove the old key)
	The secons time it calles it it expectes the NEw Key Value to put 
	into the index File.

	These important facts are esseional for any one wanting to Index on a
	UDF that does not Use the Data In the Record to build a key as in my application.


Below is an example of a program that must be aware of the above information.
This program has no other purpose other than to demontrate the above.
However This information can be usefull for a application that may want to
build a key based on the current Record Value and say the Next few
records in the same file , this is actually what I use it for.
The application Allows Names of things (peoples,movies,books etc) to be stored
and indexed in such a way that allows them to be looked up by any word
that appears in the name. ie) The Red Hot Chillie Peppers may be looked up
and browsed by any word or combination of words that appears in the name.
Prevouisly Names were stored backwards and forwards to accomplish something
close to this but nowhere near as flexibale.


******************************

Set Talk off
Set Echo off
Clear All
Set Exclusive off
Clear Program
Clear All
Clear
Set Safe off

Public _KEY   		 && The Index Key to be returned by the UDF
Public _IdxUpdate	 && Tells The UDF if it is Update Mode (Insert/Replace) or REINDEX
Public _IdxReplace	 && Update is a REPLACE otherwise a Insert
Public NeedOld       && Toggle to get New/Old Keys in a Replace
Public NEWKEY,OLDKEY && Hold the New/OLD keys


_KEY 	   	= ""    
_IdxUpdate 	= .F.
_IdxReplace = .F.

IsExCl = SET("EXCLU")=="ON"

Use  Test3 excl
Zap
Index on IndexUdf(WORD) Tag One
Use
Use  Test3  Order 1
Go Top



Set Print to test3.prn  && Save Out Put of Index UDF
Set Print on
Set Console Off

Wait "Running Test" Window NoWait


? "***************"
? "Adding Records*"
? "***************"

_IdxUpdate 	= .T. && Tell The Index UDF were Updating 
_IdxReplace = .F. && Not Replaceing Record But Adding Them

NEWKEY	=	""
OLDKEY	=	""

** IF Exclusive on Build Key After Insert is updated on moving Record Ptr
** OtherWise Build before insert

For I = 1 to 10 
	? "Before Insert Called Will Now Add Record ["+Str(i,2,0)+"]"
	IF !IsExCl
		_KEY = Upper(Padr(Num2Word(i),10," "))
	Endif
	Insert Into Test3 (Word) Values (Num2Word(i))
	IF IsExCl && If ExClusive is One Key Will Be needed After This insert 
			  && During Next Insert When Reocrd Ptr is Moved
		_KEY = Upper(Padr(Num2Word(i),10," "))
	Endif
	? "After Insert Is Called"
EndFor
_IdxUpdate   = .F.  && Done Updating
Set Order to 0
Go Top

? "***************************************"
? "The Folling is OutPut During a Replace*"
? "***************************************"

_IdxUpdate = .T.
_IdxReplace = .T.
NeedOld 	= .T.  && Intialily Index UDF will want Old Key Value

For I = 10 to 1 Step -1	  
	_NEW = Num2Word(i)
	NEWKEY = Upper(Padr(_NEW ,10," "))  && Build NEw Key Ahead of Time
	OLDKEY = Upper(Padr(WORD ,10," "))	&& Build Old Key Ahead of Time
	? "Before Replace is Called Record # ["+Str(Recno(),2,0)+"]"+" NEW Word is "+_NEW+" Old Word is "+WORD
	Replace Word With _NEW
	? "After Replace Is Called Before Skip"
	Skip
	? "After Skip"
EndFor
_IdxUpdate = .F.
_IdxReplace = .F.


Set Order to 1
Go Top
Wait "After Replace - DUPLICATE KEYS IN INDEX" Window Nowait
Set Print off
Browse
Go Top
List
Use
Set Print on
? "**************************"
? "REINDEX AFTER RELPLACE"
? "**************************"
Use Test3 Exclu Order 1

** Because _IdxUpdate is .F. The Index UDF will Build The Key

Reindex

Go Top
Wait "After Replace And Reindex" Window Nowait
Set Print off
Browse

Set Print off
Set Print to
Set Console On
Wait "See Test3.prn For Results" Window

**********************
Procedure IndexUdf
**********************
Param _WORD
If _IdxUpdate
	  if _IdxReplace
		 if NeedOld
			 _KEY = OLDKEY
		 Else
		 	_KEY = NEWKEY
		Endif
	Endif
	? "In IndexUDF - Prevously Built Key Returned By UDF is ["+_KEY+"]"+"  Recno() = ["+Str(Recno(),2,0)+"]"
	NeedOLD = !NeedOLD
	Return _KEY
 Else
	 ? "In IndexUDF - Generated and returned By UDF is ["+Upper(Padr(Word,10," "))+"]"
	 Return Upper(Padr(Word,10," "))
Endif

*********************************************
Procedure Num2Word
*********************************************
** Generates Some Test Data

Parameters Num


Do Case
Case Num=1
	_Tword = "ONE"
Case Num=2
	_Tword = "TWO"
Case Num=3
	_Tword = "THREE"
Case Num=4
	_Tword = "FOUR"
Case Num=5
	_Tword = "FIVE"
Case Num=6
	_Tword = "SIX"
Case Num=7
	_Tword = "SEVEN"
Case Num=8
	_Tword = "EIGHT"
Case Num=9
	_Tword = "NINE"
Case Num=10
	_Tword = "TEN"
EndCase
Return _Tword

****************************

Any Commets or Suggestions Please Contact


Warren Master
CIS 70713,2002
