How can I execute a custom function in Microsoft Visual FoxPro 9?

Viewed 158

Using Microsoft Visual FoxPro 9, I have a custom function, "newid()", inside of the stored procedures for Main:

function newId
parameter thisdbf
regional keynm, newkey, cOldSelect, lDone
keynm=padr(upper(thisdbf),50)
cOldSelect=alias()
lDone=.f.
do while not lDone
    select keyvalue from main!idkeys where keyname=keynm into array akey
    if _tally=0
        insert into main!idkeys (keyname) value (keynm)
        loop
    endif
    newkey=akey+1
    update main!idkeys set keyvalue=newkey where keyname=keynm and keyvalue=akey
    if _tally=1
        lDone=.t.
    endif
enddo
if not empty(cOldSelect)
    select &cOldSelect
else
    select 0
endif
return newkey

This function is used to generate a new ID for records added to the database.

It is called as the default value:

Default value for id: newid("TABLENAME")

I would like to call this newid() function and retrieve its returned value. When executing SELECT newid("TABLENAME"), the error is is thrown:

Invalid subscript reference

Enter image description here

How can I call the newid() function and return the newkey in Visual FoxPro 9?

2 Answers

As an addition to what @Stefan Wuebbe said,

You actually had your answer in your previous question here that you forgot to update.

From your previous question, as I understand you are coming from a T-SQL background. While in T-SQL (and in SQL generally) there is:

Select < anyVariableOrFunction >

that returns a single column, single row result, in VFP 'select' like that has another meaning:

Select < aliasName >

aliasName is an alias of a working area (or it could be number of a work area) and is used to change the 'current workarea'. When it was used in xBase languages like Foxpro (and dBase), those languages didn't yet meet ANSI-SQL if I am not wrong. Anyway, in VFP there are 2 Select, this one and SELECT - SQL which definitely requires a FROM clause.

VFP has direct access to variables and function calls though, through the use of = operator.

SELECT newid("TABLENAME")

in T-SQL, would be (you are just displaying the result):

? newid("TABLENAME")

To store it in a vaiable, you would do something like:

local lnId
lnId = newid("TABLENAME")
* do something with m.lnId
* Note the m. prefix, it is a built-in alias for memory variables

After having said all these, as per your code.

It looks like it has been written by a very old FoxPro programmer and I must admit I am seeing it the first time in my life that someone used "REGIONAL" keyword in VFP. It is from FoxPro 2.x days I know but I didn't see anyone use it up until now :) Anyway, that code doesn't seem to be robust enough in a multiuser environment, you might want to change it. VFP ships with a NewId sample code and below is the slightly modified version that I have been using in many locations and proved to be reliable:

Function NewID
    Lparameters tcAlias,tnCount
    Local lcAlias, lnOldArea, lcOldReprocess, lcTable, lnTagNo, lnNewValue, lnLastValue, lcOldSetDeleted
    lnOldArea = Select()
    lnOldReprocess = Set('REPROCESS')
    * Uppercase Alias name
    lcAlias = Upper(Iif(Parameters() = 0, Alias(), tcAlias))
    * Lock reprocess - try once
    Set Reprocess To 1
    If !Used("IDS")
        Use ids In 0
    Endif
    * If no entry yet create
    If !Seek(lcAlias, "Ids", "tablename")
        Insert Into ids (tablename, NextID) Values (lcAlias,0)
    Endif
    * Lock, increment id, unlock, return nextid value
    Do While !Rlock('ids')
        * Delay before next lock trial
        lnStart = Seconds()
        Do While Seconds()-lnStart < 0.01
        Enddo
    Enddo

    lnLastValue = ids.NextID
    lnNewValue  = m.lnLastValue + Evl(m.tnCount,1)

    *Try to query primary key tag for lcAlias
    lcTable = Iif( Used(lcAlias),Dbf(lcAlias), Iif(File(lcAlias+'.dbf'),lcAlias,''))
    lcTable = Evl(m.lcTable,m.lcAlias)
    If !Empty(lcTable)
        Use (lcTable) In 0 Again Alias '_GetPKKey_'
        For m.lnTagNo=1 To Tagcount('','_GetPKKey_')
            If Primary(m.lnTagNo,'_GetPKKey_')
                m.lcOldSetDeleted = Set("Deleted")
                Set Deleted Off

                Select '_GetPKKey_'
                Set Order To Tag (Tag(m.lnTagNo,'_GetPKKey_')) ;
                    In '_GetPKKey_' Descending
                Locate
                lnLastValue = Max(m.lnLastValue, Evaluate(Key(m.lnTagNo,'_GetPKKey_')))
                lnNewValue = m.lnLastValue + Evl(m.tnCount,1)

                If Upper(m.lcOldSetDeleted) == 'ON'
                    Set Deleted On
                Endif
                Exit
            Endif

        Endfor
        Use In '_GetPKKey_'
        Select ids
    Endif

    * Increment
    Replace ids.NextID With m.lnNewValue In 'ids'
    Unlock In 'ids'
    Select (lnOldArea)
    Set Reprocess To lnOldReprocess
    Return ids.NextID
Endfunc

Note: If you use this, as I see from your code, you would need to change the "id table" name to idkeys, field names to keyname, keyvalue:

ids => idKeys
tablename => keyName
nextId => keyValue

Or in your database just create a new table with this code:

CREATE TABLE ids (TableName c(50), NextId i)
INDEX on TableName TAG TableName

When executing SELECT newid("TABLENAME") The error: Invalid subscript reference is thrown

The SQL Select command in Vfp requires a From clause. Running a procedure or a function can, or better usually needs to be done differently : For example in the IDE's Command Window you can do a

? newid("xy") && the function must be "in scope",
&& i.e in your case the database that contains the "Stored
&& Procedure" must have been opened in advance 

&& or you store the function result in a variable
Local lnNextID
lnNextID = newid("xy")

Or you can use it in an SQL SELECT when you have a From alias

CREATE CURSOR placebo (col1 Int)
INSERT INTO placebo VALUES (8)
Select newid("xy") FROM placebo

Select * From placebo

Related