Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
SEEK(),INDEXSEEK() or KeyMatch() or SELECT-SQL?
Message
From
12/04/2005 04:18:55
 
General information
Forum:
Visual FoxPro
Category:
Databases,Tables, Views, Indexing and SQL syntax
Environment versions
Visual FoxPro:
VFP 8 SP1
OS:
Windows XP SP2
Miscellaneous
Thread ID:
01002645
Message ID:
01003615
Views:
29
Well, if you are happy

some thoughts

(1) I have a problem with.
luNewKey       = evaluate(m.lcAlias + "." + m.lcParentKeyExp)

What if lcParentKeyExp is bintoc(cust_id)
(2) I see you have built the table with ataginfo(). You can take it a step further (one of prev messages) (now you scan two tables, then you would scan only one)
Build one table which contains a field : Memo, Function ToCall: Cascade_parent('bintoc(fld,2)', '....', '')

for simplicity and readbility
Why not call all the necesary functions dependening on the table and triggertype ? That would take out a lot of spaghetti
Each function will test if it must do anything (keyexpr changed) and gracefully exit
select Into array (table , triggertype) 
for
   if( !eval(rtrim(aa[m.i])) )
      Success = FALSE
      exit
   endif
endfor
( I do not think that the following line takes too much time (could be wrong though)
if ( eval(keyExpr) == oldval(keyExpr) )
(3) break it down, ie why not call a function to handle the relation ? >> more readable
sample
*---------------------------------------------------------------------------
function _Trigger_Process_Relations(_table, _type)

	local Success
	Success = TRUE
	
	local TriggerArray[1], n, i
	n = 0
	
	do case
	case !m.Success
	
	case !GLOBAL_TRIGGERNODE.TriggerArray_Get(m._table, m._type, @TriggerArray, @n)
		Success = FALSE
	
	endcase
	
	for i = 1 to m.n
		
		do case
		case !m.Success
			exit
		
		case !_TriggerHistoryAdd(TriggerArray[ m.i ])
			Success = FALSE
			
		case !eval(TriggerArray[ m.i ])
			Success = FALSE
		
		case !NO_ERROR
			Success = FALSE
			
		endcase
		
		assert (m._table == CursorGetProp('SourceName')) message 'Not on table ' + m._table

	endfor
	
	return (m.Success and NO_ERROR)
	
endfunc
*---------------------------------------------------------------------------
(4) why all that testing at the beginning ?

vartype(m.tcTriggerType) = 'C'

if the trigger in the table of say customer for insert would be Trigger('CUSTOMER', 'I')
then the main entry point would be

__RI_Handler(tcTableName, tcTriggerType)


(5) throw the code away. Write your own. And include support for compound keys. Not very difficult
___________________________________________
>Gregory,
>
>Well, it took me almost the whole day to change RI builder and newri procedure. It now works much faster. For 10000 records it does update in 24 sec. I believe native RI does it in 15 sec (need to re-test). I think the speed is now acceptable, though perhaps it still could be improved. Here is my latest code:
>
>
>**__RI_HEADER!@ Do NOT REMOVE or MODIFY this line!!!! @!__RI_HEADER**
>* Steve Sawyer - Updated 11/22/1999
>* Carl Karsten - Updated 12/15/1999
>* Doug Hennig  - Updated 02/18/2000
>
>*  Program...........: NEWRI.PRG
>*  Author............: Jzanus Dev Team
>*  Project...........: Visual Collections
>*  Created...........: 04/04/2005  14:28:19
>*  Copyright.........: (c) Jzanus LTD, 2005
>*) Description.......:
>*  Calling Samples...:
>*  Parameter List....:
>*  Major change list.: Switched from scanning array of relations to table based
>
>* Known limitations:
>*	- Cascading updates and deletes for self-joins cause an "illegal recursion"
>*		error because the RI code tries to update or delete records in the same
>*		table the trigger is fired for, which VFP doesn't allow (all other
>*		rules for self-joins are handled correctly)
>*	- Compound keys aren't supported
>
>function __RI_Handler(tcTriggerType)
>
>**-- CHANGE - NN - April 04, 2005 - 14:30:12 - added a new global variable
>if vartype(m.glDontUseTriggers)="L" and m.glDontUseTriggers
>	return && Don't do any validations
>endif
>
>local lcTable, ;
>	lcAlias, ;
>	lcRecordState, ;
>	lcTriggerType, ;
>	lcDBC, ;
>	lcParent, ;
>	lcChild, ;
>	lcParentKeyExp, ;
>	lcChildKeyExp, ;
>	lcRIInfo, ;
>	luOldKey, ;
>	luNewKey, ;
>	luOldFK, ;
>	luNewFK, luKey
>
>* Define some constants that'll make the code easier to read.
>
>#define ccFLDSTATE_UNCHANGED	   '1'
>* GETFLDSTATE() 1 = the field is unchanged
>#define ccFLDSTATE_EDITDEL		   '2'
>* GETFLDSTATE() 2 = the record was edited or deletion status changed
>#define ccFLDSTATE_NEWUNCHANGED	   '3'
>* GETFLDSTATE() 3 = a new unchanged field or new undeleted record
>#define ccFLDSTATE_NEWCHANGED	   '4'
>* GETFLDSTATE() 3 = a new changed field or new deleted record
>#define cnERR_TRIGGER_FAILED	1539
>* Trigger failed error number
>
>* Get the name of the table the trigger was fired for and determine what type
>* of trigger was fired.
>
>lcTable       = upper(cursorgetprop('SourceName'))
>lcAlias       = alias()
>lcRecordState = getfldstate(-1)
>
>do case
>
>* If the trigger was passed, use it.
>
>case vartype(m.tcTriggerType) = 'C' and not empty(m.tcTriggerType)
>	lcTriggerType = upper(m.tcTriggerType)
>
>* If the deletion status was changed and the record is deleted, this is a
>* "DELETE" trigger.
>
>case left(m.lcRecordState, 1) = ccFLDSTATE_EDITDEL and deleted()
>	lcTriggerType = 'DELETE'
>
>* If the deletion status was changed and the record is not deleted, it was
>* just recalled, so this is an "INSERT" trigger.
>
>case left(m.lcRecordState, 1) = ccFLDSTATE_EDITDEL
>	lcTriggerType = 'INSERT'
>
>* If this is a new record, this is an "INSERT" trigger.
>
>case ccFLDSTATE_NEWUNCHANGED $ m.lcRecordState or ;
>		ccFLDSTATE_NEWCHANGED $ m.lcRecordState
>	lcTriggerType = 'INSERT'
>
>* Some field in the table has been changed, so this is an "UPDATE" trigger.
>
>case ccFLDSTATE_EDITDEL $ m.lcRecordState
>	lcTriggerType = 'UPDATE'
>
>* Carl Karsten found a weird bug in VFP: if you have a table with a memo field
>* and delete a record such that the subsequent records have to be physically
>* moved when you PACK, the "UPDATE" trigger for the table fires when you move
>* the record pointer or close the table. In that case, we'll ignore it.
>
>case m.lcRecordState = replicate(ccFLDSTATE_NEWUNCHANGED, len(m.lcRecordState))
>	return
>endcase
>
>
>* If we're at the top trigger level, start a transaction, create an error flag
>* and array variables, get a snapshot of open tables, and set up the
>* environment the way we need it.
>local llManualCleanUp, loSession
>llManualCleanUp = .f.
>
>if _triggerlevel = 1
>	private plError
>	plError = .f.
>	release gaErrors
>	public gaErrors[1, 12]
>*	try
>*		loSession = newobject('aSessionEnvironment','aEnviron.vcx')
>*	catch
>	llManualCleanUp = .t.
>	private paUsed[1], ;
>		pcExact, ;
>		pcANSI, ;
>		pcDeleted, ;
>		pcOnEscape, ;
>		pcError, ;
>		pcOldDBC, plError
>
>	aused(paUsed)
>	pcExact  = set('EXACT')
>	pcANSI   = set('ANSI')
>	pcDeleted = set('DELETED')
>	pcError   = on('ERROR')
>	pcOnEscape = on('escape')
>	on escape RICleanUp(.t.)
>*	endtry
>
>	set exact on
>	set ansi on
>	set deleted on
>	on error LogRIError(error(),message()) &&plError = .t.
>	begin transaction
>endif
>
>if vartype(m.plError) = "L" and m.plError
>** Error occurred
>else
>	pcOldDBC = iif(empty(dbc()), '', '"' + dbc() + '"')
>	lcDBC    = cursorgetprop('Database')
>	set database to (m.lcDBC)
>	if not used('RIDefinitions')
>		use RIDefinitions in 0 && Open table with RI Definitions
>	endif
>	local lcChildTag, lcParentTag
>	store "" to lcParentKeyExp, lcChildKeyExp
>	select RIDefinitions
>	lcParent = m.lcTable
>	scan for upper(ParentTB) == padr(m.lcTable,128) and not m.plError
>		lcChild  = alltrim(ChildTB)
>		lcParentTag  = alltrim(ParentTag)
>		lcChildTag  = alltrim(ChildTag)
>		lcChildKeyExp = alltrim(ChildKey)
>
>		do case
>
>* If this is an update trigger and this relation has our table as the parent,
>* let's process it. We'll ignore it if the RI rule is empty or "ignore".
>
>		case m.lcTriggerType = 'UPDATE'
>			lcRIInfo = UpdTrigger
>			if not empty(m.lcRIInfo) and m.lcRIInfo <> 'I'
>				if not alltrim(ParentKey) == m.lcParentKeyExp && So it would calculate it only once
>					lcParentKeyExp = alltrim(ParentKey)
>					luOldKey       = oldval(m.lcParentKeyExp, m.lcAlias)
>					luNewKey       = evaluate(m.lcAlias + "." + m.lcParentKeyExp)
>				endif
>
>* If this is a self-join, we may have an update trigger because the FK field
>* in the "child" copy of the table was changed (which really is an insert
>* trigger), so let's handle it if we have a restrict insert rule. The reason
>* we don't handle it in a CASE below is that the user may have changed both
>* the parent and foreign key fields.
>
>				if m.lcParent == m.lcChild && very rare case
>					luOldFK = oldval(m.lcChildKeyExp, m.lcAlias)
>					luNewFK = evaluate(m.lcAlias + "." + m.lcChildKeyExp)
>					if m.luOldFK <> m.luNewFK and ;
>							substr(m.lcRules, cnRIINFO_INSERT, 1) = 'R'
>						Restrict_Insert(m.lcParent, m.lcChild, m.lcParentKeyExp, ;
>							m.lcChildKeyExp, m.luNewFK, m.lcTriggerType, m.lcParentTag )
>					endif luOldFK <> luNewFK ...
>				endif lcParent = lcChild
>
>				do case
>
>* The parent key wasn't changed or an error occurred, so we have nothing to do.
>
>				case m.plError or m.luOldKey = m.luNewKey
>
>* If the parent key has been changed, call the appropriate function, depending
>* on whether this is a cascade or restrict rule.
>
>				case m.lcRIInfo = 'C'
>
>					Cascade_Update(m.lcParent, m.lcChild, m.lcParentKeyExp, ;
>						m.lcChildKeyExp, m.luOldKey, m.luNewKey)
>
>				case m.lcRIInfo = 'R'
>					Restrict_Update(m.lcParent, m.lcChild, m.lcParentKeyExp, ;
>						m.lcChildKeyExp, m.luOldKey, m.luNewKey, m.lcChildTag)
>				endcase
>			endif not empty(lcRIInfo) ...
>
>* If this is a delete trigger and this relation has our table as the parent,
>* let's process it. We'll ignore it if the RI rule is empty or "ignore".
>
>		case m.lcTriggerType = 'DELETE'
>			lcRIInfo = DelTrigger
>			if not empty(m.lcRIInfo) and m.lcRIInfo <> 'I'
>				if not alltrim(ParentKey) == m.lcParentKeyExp and empty(m.luKey)&& So it would calculate it only once
>					lcParentKeyExp = alltrim(ParentKey)
>					luKey          = evaluate(m.lcAlias + "." + m.lcParentKeyExp)
>				endif
>
>* Call the appropriate function, depending on whether this is a cascade or
>* restrict rule.
>
>				do case
>				case m.lcRIInfo = 'C'
>					Cascade_Delete(m.lcParent, m.lcChild, m.lcParentKeyExp, ;
>						m.lcChildKeyExp, m.luKey)
>				case m.lcRIInfo = 'R'
>					Restrict_Delete(m.lcParent, m.lcChild, m.lcParentKeyExp, ;
>						m.lcChildKeyExp, m.luKey, m.lcChildTag)
>				endcase
>			endif not empty(lcRIInfo) ...
>		endcase
>
>	endscan
>
>** Now do the same for the child
>	lcChild = m.lcTable
>	lcChildKeyExp = ""
>
>	scan for upper(ChildTB) == padr(m.lcTable,128) and not m.plError
>		lcParent = alltrim(ParentTB)
>		lcParentTag  = alltrim(ParentTag)
>		lcChildTag  = alltrim(ChildTag)
>		lcParentKeyExp = alltrim(ParentKey)
>
>* If this relation has our table as the child, let's process it. We'll only
>* process a "restrict" rule in either an insert or update trigger.
>
>		lcRIInfo = InsTrigger
>		if m.lcRIInfo = 'R'
>			if not m.lcChildKeyExp == alltrim(ChildKey) && we don't want to evaluate more times than needed
>				lcChildKeyExp = alltrim(ChildKey)
>				luKey     = evaluate(m.lcAlias + "." + m.lcChildKeyExp)
>			endif
>
>* If this is an insert trigger or if it's an update trigger and the foreign key
>* has changed, call the Restrict_Insert function to ensure the foreign key
>* exists in the parent table.
>
>			if m.lcTriggerType <> 'UPDATE' or oldval(m.lcChildKeyExp, m.lcAlias) <> m.luKey
>				Restrict_Insert(m.lcParent, m.lcChild, m.lcParentKeyExp, ;
>					m.lcChildKeyExp, m.luKey, m.lcTriggerType, m.lcParentTag)
>			endif lcTriggerType <> 'UPDATE' ...
>		endif lcRIInfo = 'R'
>	endscan
>* If we're at the top trigger level, either end the transaction or roll it
>* back, depending on whether the trigger succeeded or not, close any tables we
>* opened, restore the things we changed, and return whether we succeeded or
>* not.
>	if m.llManualCleanUp
>		=RICleanUp(.f.)
>	else
>		if m.plError
>			rollback
>		else
>			end transaction
>		endif plError
>	endif
>
>* If we're not at the top trigger level, return .T. so we don't trigger an
>* error yet.
>endif _triggerlevel = 1
>return not m.plError
>
>* CleanUp procedure
>function RICleanUp(tlEscaped)
>if m.tlEscaped and messagebox("Are you sure you want to stop the trigger's execution?",4+16,"Stop execution") = 7
>**
>	retry
>else
>
>	local lnTables, laTables[1], lnI
>	if not empty(m.pcOldDBC)
>		set database to (m.pcOldDBC)
>	endif
>	if _triggerlevel = 1
>		if m.plError or m.tlEscaped
>			rollback
>		else
>			end transaction
>		endif plError
>** Gregory Adam suggested in the thread #1002645 to only close tables in Default DS
>		if set("Datasession") > 1 && we're dealing with private DS, don't need to close
>		else
>			lnTables = aused(laTables)
>			for lnI = 1 to m.lnTables
>				lcTable = laTables[m.lnI, 1]
>				if not empty(m.lcTable) and ascan(paUsed, m.lcTable) = 0
>					use in (m.lcTable)
>				endif not empty(lcTable) ...
>			next lnI
>
>		endif
>		if m.pcExact = "OFF"
>			set exact off
>		endif
>		if m.pcANSI = "OFF"
>			set ansi off
>		endif
>		if m.pcDeleted = "OFF"
>			set deleted off
>		endif
>		on error &pcError
>		on escape &pcOnEscape
>	endif
>endif
>return
>
>* Cascade update function: change the foreign key field in all child records
>* that belong to the parent record.
>
>function Cascade_Update(tcParentTable, ;
>	tcChildTable, ;
>	tcParentKey, ;
>	tcChildKey, ;
>	tuOldKey, ;
>	tuNewKey)
>local laError[1]
>
>* Do the cascading update. Log any error that occurred.
>if isnull(m.tuOldKey )
>	update (m.tcChildTable) ;
>		set &tcChildKey = m.tuNewKey ;
>		where &tcChildKey is null
>else
>	update (m.tcChildTable) ;
>		set &tcChildKey = m.tuNewKey ;
>		where &tcChildKey = m.tuOldKey
>endif
>
>if m.plError
>	aerror(laError)
>	LogRIError(laError[1], laError[2], 'Cascade Update', ;
>		m.tcParentTable, iif(used(m.tcParentTable),recno(m.tcParentTable),0), ;
>		m.tcParentKey, m.tuNewKey, ;
>		m.tcChildTable, 0, m.tcChildKey, m.tuOldKey)
>endif plError
>return
>
>* Cascade delete function: delete all child records that belong to the parent
>* record.
>
>function Cascade_Delete(tcParentTable, ;
>	tcChildTable, ;
>	tcParentKey, ;
>	tcChildKey, ;
>	tuOldKey, ;
>	tuNewKey)
>local laError[1]
>
>if isnull(m.tuOldKey)
>	delete ;
>		from (m.tcChildTable) ;
>		where &tcChildKey is null
>else
>	delete ;
>		from (m.tcChildTable) ;
>		where &tcChildKey = m.tuOldKey
>endif
>if m.plError
>	aerror(laError)
>	LogRIError(laError[1], laError[2], 'Cascade Delete', ;
>		m.tcParentTable, iif(used(m.tcParentTable),recno(m.tcParentTable),0), ;
>		m.tcParentKey, m.tuNewKey, ;
>		m.tcChildTable, 0, m.tcChildKey, m.tuOldKey)
>endif plError
>return
>
>* Restrict delete function: see if there are any records in the specified child
>* table matching the specified key in the parent table.
>
>function Restrict_Delete(tcParentTable, ;
>	tcChildTable, ;
>	tcParentKey, ;
>	tcChildKey, ;
>	tuKey, tcTag)
>local lcTable
>
>if not isnull(m.tuKey)
>	lcTable = strtran(m.tcChildTable, ' ', '_')
>
>	if not used(m.lcTable)
>		use (m.tcChildTable) again in 0 shared alias (m.lcTable)
>	endif not used(lcTable)
>*	if indexseek(m.tuKey, .f., m.lcTable, m.tcTag) && Record exists
>	if keymatch(m.tuKey,tagno(m.tcTag, '', m.lcTable), m.lcTable) && Record exists
>		plError = .t.
>		LogRIError(cnERR_TRIGGER_FAILED, 'Trigger Failed', 'Restrict Delete', ;
>			m.tcParentTable, iif(used(m.tcParentTable),recno(m.tcParentTable),0), ;
>			m.tcParentKey, m.tuKey, ;
>			m.tcChildTable, 0, m.tcChildKey, m.tuKey)
>	endif _tally > 0
>endif
>return
>
>* Restrict update function: see if there are any records in the specified child
>* table matching the specified key in the parent table.
>
>function Restrict_Update(tcParentTable, ;
>	tcChildTable, ;
>	tcParentKey, ;
>	tcChildKey, ;
>	tuOldKey, ;
>	tuNewKey, tcTag)
>*local laCount[1]
>local lcTable
>
>if not isnull(m.tuOldKey)
>	lcTable = strtran(m.tcChildTable, ' ', '_')
>
>	if not used(m.lcTable)
>		use (m.tcChildTable) again in 0 shared alias (m.lcTable)
>	endif not used(lcTable)
>
>*	if indexseek(m.tuOldKey, .f., m.lcTable, m.tcTag) && Record exists
>	if keymatch(m.tuOldKey,tagno(m.tcTag, '', m.lcTable), m.lcTable) && Record exists
>		plError = .t.
>		LogRIError(cnERR_TRIGGER_FAILED, 'Trigger Failed', 'Restrict Update', ;
>			m.tcParentTable, iif(used(m.tcParentTable),recno(m.tcParentTable),0), ;
>			m.tcParentKey, m.tuNewKey, ;
>			m.tcChildTable, 0, m.tcChildKey, m.tuOldKey)
>	endif _tally > 0
>endif
>return
>
>* Restrict insert function: ensure a record in the parent table for the foreign
>* key exists.
>* CFK - 5/28
>* Or if the foreign key is null, no parent record is needed.
>* The logic is this:
>*  if you specify in the dbc that a child cannot exist with out a parent,
>*    you can laxly enforce that by allowing nulls in that feild
>*    and this code will allow that setting to be overridden
>*    by setting the foreign key to null
>*  or,
>*  you can strictly enforce it by not allowing nulls, and then it will
>*    pass this test, but fail the "allow nulls" test that the
>*    database engine enforces.
>
>function Restrict_Insert(tcParentTable, ;
>	tcChildTable, ;
>	tcParentKey, ;
>	tcChildKey, ;
>	tuKey, ;
>	tcTriggerType, tcTag)
>
>local lcTable
>
>* CFK - 5/28
>* If the key is Null, don't check for a parent, let it pass.
>if !isnull( m.tuKey )
>
>	lcTable = strtran(m.tcParentTable, ' ', '_')
>
>	if not used(m.lcTable)
>		use (m.tcParentTable) again in 0 shared alias (m.lcTable)
>	endif not used(lcTable)
>
>*	if not indexseek(m.tuKey,.f.,m.lcTable,m.tcTag)
>	if not keymatch(m.tuKey,tagno(m.tcTag, '', m.lcTable), m.lcTable) && Record doesn't exist
>		plError = .t.
>		LogRIError(cnERR_TRIGGER_FAILED, 'Trigger Failed', 'Restrict ' + ;
>			proper(m.tcTriggerType), m.tcParentTable, 0, m.tcParentKey, 'Not Found', ;
>			m.tcChildTable, iif(used(m.tcChildTable),recno(m.tcChildTable),0), ;
>			m.tcChildKey, m.tuKey)
>	endif laCount[1] = 0
>endif  !isnull( tuKey )
>
>return
>
>* Log errors to the public gaError array.
>
>procedure LogRIError(tnErrNo, ;
>	tcMessage, ;
>	tcCode, ;
>	tcParentTable, ;
>	tnParentRec, ;
>	tcParentExp, ;
>	tuParentKey, ;
>	tcChildTable, ;
>	tnChildRec, ;
>	tcChildExp, ;
>	tuChildKey)
>local lnErrorRows, ;
>	lnLevel
>
>plError = .t.
>* Add another row to the error array if necessary.
>
>lnErrorRows = alen(gaErrors, 1)
>if type('gaErrors[lnErrorRows, 1]') <> 'L'
>	lnErrorRows = lnErrorRows + 1
>	dimension gaErrors[lnErrorRows, alen(gaErrors, 2)]
>endif type('gaErrors[lnErrorRows, 1]') <> 'L'
>
>* Log the error information, including the parameters passed to us and the
>* program stack.
>
>gaErrors[lnErrorRows,  1] = tnErrNo
>gaErrors[lnErrorRows,  2] = tcMessage
>gaErrors[lnErrorRows,  3] = evl(tcCode,"")
>gaErrors[lnErrorRows,  4] = ''
>for lnLevel = 1 to program(-1)
>	gaErrors[lnErrorRows,  4] = gaErrors[lnErrorRows, 4] + ',' + ;
>		program(lnLevel)
>next lnLevel
>gaErrors[lnErrorRows,  5] = evl(tcParentTable,"")
>gaErrors[lnErrorRows,  6] = evl(tnParentRec,0)
>gaErrors[lnErrorRows,  7] = evl(tuParentKey,"")
>gaErrors[lnErrorRows,  8] = evl(tcParentExp,"")
>gaErrors[lnErrorRows,  9] = evl(tcChildTable,"")
>gaErrors[lnErrorRows, 10] = evl(tnChildRec,0)
>gaErrors[lnErrorRows, 11] = evl(tuChildKey,"")
>gaErrors[lnErrorRows, 12] = evl(tcChildExp,"")
>return tnErrNo
>
>**__RI_FOOTER!@ Do NOT REMOVE or MODIFY this line!!!! @!__RI_FOOTER**
>
>
>>>>>>Two options
>>>>>>(1) for runtime, is to open all the tables at startup of the app in a private datasession
>>>>>>
>>>>>>(2) in the trigger open them if you need them, then after rollback/end transaction only close them if it is the default datasession. That way forms will go faster and when you develop, the default datasession will be cleaner
>>>>>>
>>>>>>
>>>>>>#define PRIVATEDATASESSION ( set('DataSession') > 1 )
>>>>>>....
>>>>>>  if( _triggerlevel = 1 )
>>>>>>      if (   )
>>>>>>          rollback
>>>>>>      else
>>>>>>         end transaction
>>>>>>      endif
>>>>>>
>>>>>>
>>>>>>     if( PRIVATEDATASESSION )
>>>>>>        close all table opened by me
>>>>>>     endif
>>>>>>
>>>>>
>>>>>Good idea. So far I ran my tests in the default DS. I would add that check, thanks.
>>>>
>>>>_________________________
>>>>hey, you're welcome
>>>>
>>>>I close all the tables when I exit the top level of my RI whether private datasession or not. Neither do I open all the tables at the start of the app
>>>>
>>>>When you have done some tests with say (1) and (2) would you give some feedback ? ie compare (0) nothing, (1) and (2) local on a network. If there's a significant difference, I may switch to scenario (1) or (2) whatever is the most performant. Thanks
>>>
>>>Gregory,
>>>
>>>I'm only going to try number 2. I'm going to run just few tests, because this issue is taking too much of my time. We discussed this problem today on our programming meeting. I told, that I want to use help of one other programmer and want to do a parallel programming with him. Basically, it's obvious to me that there is a bug in the existing code, which I need to trace first. Why I know about the bug? Well, it's easy: in my tests I was changing the value of cEntered_User field. This field is not engaged in any relations, but yet I ended up with lots of tables open. So there is clearly a bug which we have to trace. Once this done, I want to modify the builder, so it would create a table in the DBC() path called RIDefinitions. In this table I would only put those RI that have R or C. E.g. this table will have fewer records than number of relations, because more than half of them have Ignore for all three types. I also put Key expressions for both parent and child, so I would not need
>>to
>>> resolve them at run-time. So all these changes are going to speed up the trigger's code. I still would have just one procedure.
>>>
>>>That's my plan, now I'm going to call my colleague and start implementing it.
>>>
>>>Thanks again for lots of useful ideas.
>>_______________
>>
>>Some rules need two functions, eg a restrict on a child insert implies a restrict on the parent update
>>_______________
>>about that tables open thing (cEntered_User field)
>>
>>I have not gone through all the code, just throwing in some ideas, but opening tables that are not needed may have some causes
>>
>>(1) open a related child table when there is no delete trigger
>>(2) open a realted parent table when there is no insert trigger
>>
>>(3) open a related parent and/or child table for an update trigger when the index has not changed, eg
>>
>>function _Trigger_Process_Relation_Parent_Update_Restrict
>>		lparameters	TRIGGERRELATION_PARAMETERLIST
>>
>>	local Success, sOther, _error_msg, s, ParentValue
>>	Success = TRUE
>>	s = select(0)
>>	ParentValue = oldval(m.thisIndexExpr)
>>	
>>	do case
>>	case !m.Success
>>	
>>	case eval(m.thisIndexExpr) == m.ParentValue
>>		&& not changed                         &&& HERE, it falls through, not opening anything
>>
>>        case !_TriggerOpen(@sOther, m.OtherTable, m.OtherTag )
>>		Success = FALSE
>>			
>>	otherwise
>>          ....
>>        endcase
>>
>>
>>       return (m.Success and NO_ERROR)
>>
>>
>>
Gregory
Previous
Next
Reply
Map
View

Click here to load this message in the networking platform