command to find the number of entries in a temp table - progress-4gl

What is the command to find the number of entries/rows in a temp table? version 10.2b

/* create a temp-table so that we can test this technique
*/
define temp-table ttTest
field id as int
.
create ttTest.
id = 1.
create ttTest.
id = 2.
/* how many records?
*/
define query q for ttTest cache 0.
open query q preselect each ttTest.
display num-results( "q" ).

or you can use clasic FOR EACH:
DEFINE VARIABLE iCount AS INT NO-UNDO.
FOR EACH ttTest:
iCount = iCount + 1.
END.
DISPLAY iCount.

Here's mine, that works for any temp-table :
FUNCTION TT_NBREC RETURNS INTEGER ( INPUT pr_hd_temptable AS HANDLE ) :
DEFINE VARIABLE in_nbrec AS INTEGER NO-UNDO INITIAL 0.
DEFINE VARIABLE hd_buffer AS HANDLE NO-UNDO.
DEFINE VARIABLE hd_query AS HANDLE NO-UNDO.
DEFINE VARIABLE ch_query AS CHARACTER NO-UNDO.
DEFINE VARIABLE ch_table AS CHARACTER NO-UNDO.
DEFINE VARIABLE lg_error AS LOGICAL NO-UNDO.
ASSIGN
ch_table = pr_hd_temptable:NAME
ch_query = "FOR EACH " + ch_table + " NO-LOCK".
CREATE BUFFER hd_buffer FOR TABLE ch_table.
CREATE QUERY hd_query.
hd_query:ADD-BUFFER( hd_buffer ).
lg_error = hd_query:QUERY-PREPARE( ch_query ) NO-ERROR.
hd_query:QUERY-OPEN().
hd_query:GET-FIRST().
DO WHILE NOT hd_query:QUERY-OFF-END :
ASSIGN in_nbrec = in_nbrec + 1.
hd_query:GET-NEXT().
END.
hd_query:QUERY-CLOSE().
DELETE OBJECT hd_query.
DELETE OBJECT hd_buffer.
ASSIGN
hd_query = ?
hd_buffer = ?.
RETURN in_nbrec.
END FUNCTION.
Just pass it the handle of your temp-table and you get the number of records.
It can certainly be improved, but it works fast enough for me.

Related

Progress 4GL - update

how can I check if update was successful when I run this For Each
FOR EACH products
WHERE products.name = "ProductsName":
update price = 1000.
END.
Sometimes this For Each is ok, but sometimes when record is lock it doesn't work. I need run this For Each via WebSpeed and return true when For Each is successful or false when not. How can I get this result?
You should add more details to your request, but try this it might help get you started:
procedure update_items:
define output parameter records_read as integer no-undo.
define output parameter records_updated as integer no-undo.
define output parameter records_locked as integer no-undo.
define buffer item for item.
define buffer item_update for item.
define variable retry_count as integer no-undo.
for each item no-lock:
accumulate 1 (total).
records_read = (accum total 1).
retry_count = 0.
repeat for item_update:
find item_update exclusive-lock
where rowid(item_update) = rowid(item)
no-wait no-error.
if locked item_update then do:
if retry_count > 5 then do:
records_locked = records_locked + 1.
leave.
end.
retry_count = retry_count + 1.
do on endkey undo, leave:
pause 3 no-message.
end.
undo, next.
end.
if not available item_update then do:
/*If that matters you can code for it too*/
leave.
end.
item_update.Price = 1000.
release item_update.
records_updated = records_updated + 1.
leave.
end.
end.
end.
define variable items_read as integer no-undo.
define variable items_updated as integer no-undo.
define variable items_locked as integer no-undo.
run update_items(output items_read,
output items_updated,
output items_locked).
display items_read items_updated items_locked with side-labels 1 col.

How to get the field name dynamically and update it to main table in progress

Program:It is a just maintenance program, in this one it displays the Item Code in one frame and it prompt for the input. if you enter the item code it has to displays what are the blank fields for that record in pt_mstr and display in one frame(No need to display all blank fields, just first 4 or 5 fields enough). and also in that frame only if user want to update it update directly to main table pt_mstr.
What i tried is, i just write the code for getting blank fields using buffer handle and after that i create one temp table and displaying the fields, i strucked there itself, i am unable to update fields.
My code:
/*Sample Item master Maintenance Program*/
/* DISPLAY TITLE */
{us/mf/mfdtitle.i "3+ "}
DEFINE VARIABLE hBuffer AS HANDLE NO-UNDO.
DEFINE VARIABLE i AS INTEGER NO-UNDO.
DEFINE VARIABLE j AS INTEGER NO-UNDO.
DEFINE VARIABLE hField AS HANDLE NO-UNDO.
define variable fldnm as character extent 10 no-undo.
define temp-table tt_temp no-undo
field tt_part like pt_part
field field1 as char extent 10.
form
pt_part colon 25
with frame a side-labels width 80.
setFrameLabels(frame a:handle).
/* DISPLAY */
view frame a.
repeat with frame a:
prompt-for pt_part
editing:
/* FIND NEXT/PREVIOUS RECORD */
{us/mf/mfnp.i pt_mstr pt_part "pt_mstr.pt_domain = global_domain and pt_part" pt_part pt_part pt_part }
if recno <> ? then
do:
display pt_part.
find pt_mstr where pt_part = input pt_part and pt_domain=global_domain no-lock no-error.
ASSIGN hBuffer = BUFFER pt_mstr:HANDLE.
empty temp-table tt_temp.
j = 1.
DO i = 1 TO 10:
ASSIGN hField = hBuffer:BUFFER-FIELD(i).
IF ((hField:BUFFER-VALUE = "" )) THEN
do:
/* message hField:NAME "test" view-as alert-box.*/
find first tt_temp where tt_part = pt_part no-lock no-error.
if not avail tt_temp then
do:
create tt_temp.
assign
tt_part = pt_part
field1[j] = hField:NAME.
j = j + 1.
end.
else do:
assign
field1[j] = hField:NAME.
j = j + 1.
end.
end.
end.
end.
for each tt_temp:
display field1[1] field1[2] field1[3] field1[4].
end.
end.
end.
Are you sure you need your temp-tables to do this? I've created an example using only the actual table (but created a fake temp-table instead). You would have to look into data error handling, data validation, transaction, locking etc before putting this into production of course.
/*First we need some fake data */
DEFINE TEMP-TABLE ttMockedData NO-UNDO
FIELD id AS INTEGER
FIELD dataName AS CHARACTER FORMAT "x(8)"
FIELD dataType AS CHARACTER FORMAT "x(8)"
FIELD dataDescrioption AS CHARACTER FORMAT "x(32)".
DEFINE VARIABLE iId AS INTEGER NO-UNDO.
DEFINE VARIABLE iSearch AS INTEGER NO-UNDO LABEL "Search".
PROCEDURE createData:
DEFINE INPUT PARAMETER pcName AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER pcType AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER pcDesc AS CHARACTER NO-UNDO.
iId = iId + 1.
CREATE ttMockedData.
ASSIGN
ttMockedData.id = iId
ttMockedData.dataName = pcName
ttMockedData.dataType = pcType
ttMockedData.dataDesc = pcDesc.
END PROCEDURE.
RUN createData("Test 1", "TESTTYPE", "A TEST").
RUN createData("Test 2", "", "ANOTHER TEST").
RUN createData("", "TESTTYPE 2", "").
RUN createData("4", "", "").
/* Program starts here */
updating:
REPEAT:
UPDATE iSearch WITH FRAME x0.
IF iSearch > 0 THEN DO:
FIND FIRST ttMockedData NO-LOCK WHERE ttMockedData.id = iSearch NO-ERROR.
IF NOT AVAILABLE ttMockedData THEN DO:
MESSAGE "Not found" VIEW-AS ALERT-BOX ERROR.
RETURN ERROR.
END.
ELSE DO:
DISP ttMockedData WITH FRAME x1 1 COLUMNS SIDE-LABELS.
/* Is there an empty field? - Then we update! */
IF ttMockedData.dataName = ""
OR ttMockedData.dataType = ""
OR ttMockedData.dataDescrioption = "" THEN DO:
DISPLAY
ttMockedData.dataName
ttMockedData.DataType
ttMockedData.dataDesc
WITH FRAME x2 1 COLUMN SIDE-LABELS TITLE "Complete the data...".
/* This isn't working with temp-tables of course! */
/* Just here to make sure you handle locking! */
FIND CURRENT ttMockedData EXCLUSIVE-LOCK.
UPDATE
ttMockedData.dataName WHEN ttMockedData.dataName = ""
ttMockedData.DataType WHEN ttMockedData.DataType = ""
ttMockedData.dataDesc WHEN ttMockedData.dataDesc = ""
WITH FRAME x2.
/* This isn't working with temp-tables of course! */
/* Just here to make sure you handle locking! */
FIND CURRENT ttMockedData NO-LOCK.
END.
END.
END.
ELSE LEAVE updating.
END.

how to create a temp table with dynamic field number in progress (openedge 4gl)

how to use that with out define a temp table...
CREATE TEMP-TABLE tt.
Lifted straight from the documentation:
DEFINE VARIABLE tth AS HANDLE NO-UNDO.
DEFINE VARIABLE bh AS HANDLE NO-UNDO.
DEFINE VARIABLE qh AS HANDLE NO-UNDO.
DEFINE VARIABLE buf-ord-hndl AS HANDLE NO-UNDO.
DEFINE VARIABLE buf-rep-hndl AS HANDLE NO-UNDO.
DEFINE VARIABLE fld1 AS HANDLE NO-UNDO.
DEFINE VARIABLE fld2 AS HANDLE NO-UNDO.
/* Get database table handles */
buf-ord-hndl = BUFFER Order:HANDLE.
buf-rep-hndl = BUFFER SalesRep:HANDLE.
/* Create an empty, undefined TEMP-TABLE */
CREATE TEMP-TABLE tth.
/* Give it Order table’s fields & indexes */
tth:CREATE-LIKE(buf-ord-hndl).
/* Add field like SalesRep.RepName */
tth:ADD-LIKE-FIELD("RepName","SalesRep.RepName").
/* No more fields will be added */
tth:TEMP-TABLE-PREPARE("ordx").
/* Get the buffer handle for the temp-table */
bh = tth:DEFAULT-BUFFER-HANDLE.
/* Populate the temp-table from order */
FOR EACH Order NO-LOCK:
bh:BUFFER-CREATE.
bh:BUFFER-COPY(buf-ord-hndl).
/* Add the corresponding salesrep name */
FIND SalesRep NO-LOCK WHERE SalesRep.SalesRep = Order.SalesRep NO-ERROR.
IF AVAILABLE SalesRep THEN
bh:BUFFER-COPY(buf-rep-hndl,?,"RepName,repname").
END.
/* Run a query to access the TEMP-TABLE */
CREATE QUERY qh.
qh:SET-BUFFERS(bh).
qh:QUERY-PREPARE("FOR EACH ordx WHERE ordx.OrderNum < 50 BY ordx.RepName").
qh:QUERY-OPEN().
fld1 = bh:BUFFER-FIELD("OrderNum").
fld2 = bh:BUFFER-FIELD("RepName").
/* Display the order number and the salesrep name */
REPEAT:
qh:GET-NEXT().
IF qh:QUERY-OFF-END THEN LEAVE.
DISPLAY fld1:BUFFER-VALUE() FORMAT "X(10)".
DISPLAY fld2:BUFFER-VALUE() FORMAT "X(20)".
END.
qh:QUERY-CLOSE().
bh:BUFFER-RELEASE().
DELETE OBJECT tth.
DELETE OBJECT qh.
If you are building from scratch you probably want to be using ADD-NEW-FIELD() rather than ADD-LIKE-FIELD().
If you have an XML schema you can also potentially use that to create the TT via something like:
DEFINE VARIABLE lRetOK AS LOGICAL NO-UNDO.
DEFINE VARIABLE cSourceType AS CHARACTER NO-UNDO.
DEFINE VARIABLE cFile AS CHARACTER NO-UNDO.
DEFINE VARIABLE lOverrideDefaultMapping AS LOGICAL NO-UNDO.
DEFINE VARIABLE cFieldTypeMapping AS CHARACTER NO-UNDO.
DEFINE VARIABLE cVerifySchemaMode AS CHARACTER NO-UNDO.
DEFINE VARIABLE hTable AS HANDLE NO-UNDO.
CREATE TEMP-TABLE hTable.
ASSIGN
cSourceType = "file"
cFile = "ttcust.xsd"
lOverrideDefaultMapping = FALSE
cFieldTypeMapping = "address2,CLOB"
cVerifySchemaMode = ?.
lRetOK = hTable:READ-XMLSCHEMA (cSourceType, cFile, lOverrideDefaultMapping,
cFieldTypeMapping,cVerifySchemaMode).

How to test if string is numeric using Progress 4GL

Does Progress 4GL have a function for testing whether a string is numeric, like PHP's is_numeric($foo) function?
I've seen the function example at http://knowledgebase.progress.com/articles/Article/P148549 to test if a character in a string is numeric. Looks like it has a typo, btw.
But I would think the language would be a built-in function for this.
I was looking at this myself recently. The approved answer given to this doesn't work in 100% situations.
If the user enters any of the following special string characters: ? * - or + the answer won't work.
A single plus or minus(dash) is converted to 0 which you may not want.
A single question mark character is valid value which progress recognises as unknown value at which again you may not want.
A single or group asterisks on their own also get converted to 0.
If you run the following code you'll see what I mean.
DISP DECIMAL("*")
DECIMAL("**")
DECIMAL("?")
DECIMAL("+")
DECIMAL("-").
The following additional code maybe useful to get around this
DEFINE VARIABLE iZeroCode AS INTEGER NO-UNDO.
DEFINE VARIABLE iNineCode AS INTEGER NO-UNDO.
DEFINE VARIABLE chChar AS CHARACTER NO-UNDO.
ASSIGN iZeroCode = ASC("0")
iNineCode = ASC("9")
chChar = SUBSTRING(cNumber,1,1).
IF NOT(ASC(chChar) >= iZeroCode AND ASC(chChar) <= iNineCode) THEN DO:
MESSAGE "Invalid Number..." VIEW-AS ALERT-BOX.
END.
Do not need a function can jsut do a straight conversion.
ASSIGN dNumber = DECIMAL(cNumber) NO-ERROR.
IF ERROR-STATUS:ERROR THEN
DO:
{Handle issues}
END.
or if it is always whole numbers can use INTEGER instead of DECIMAL.
The language does not have a built-in "isNum()" type of function.
An alternative to the kbase method would be:
function isNum returns logical ( input s as character ):
define variable n as decimal no-undo.
assign
n = decimal( s )
no-error
.
return ( error-status:num-messages = 0 ).
end.
display isNum( "123" ) isNum( "xyz" ).
This code handles any numeric strings - even if the used Character is longer than the max Decimal length etc.
FUNCTION isNumeric RETURNS LOGICAL (textvalue AS CHAR):
DEF VAR i AS INT NO-UNDO.
IF textvalue = ? THEN RETURN TRUE.
DO i = 1 TO (LENGTH(textvalue) - 1):
INT(SUBSTRING(textvalue, i, (i + 1))) NO-ERROR.
IF ERROR-STATUS:ERROR THEN RETURN FALSE.
END.
RETURN TRUE.
END FUNCTION.
Works 100% of the time
FUNCTION is-num RETURNS LOGICAL
(INPUT cString AS CHARACTER):
DEFINE VARIABLE iZeroCode AS INTEGER NO-UNDO.
DEFINE VARIABLE iNineCode AS INTEGER NO-UNDO.
DEFINE VARIABLE cChar AS CHARACTER NO-UNDO.
DEFINE VARIABLE iCount AS INTEGER NO-UNDO.
DO iCount = 1 TO LENGTH(cString):
ASSIGN iZeroCode = ASC("0")
iNineCode = ASC("9")
cChar = SUBSTRING(cString,iCount,1).
IF NOT(ASC(cChar) >= iZeroCode AND ASC(cChar) <= iNineCode) THEN DO:
RETURN FALSE.
END.
END.
RETURN TRUE.
END.

Reference a table through a string representation of the name

I'm trying to write a modular procedure, where I pass in a string representation of a table name, and query/update that table.
What I tried was something to this affect:
PROCEDURE foo:
DEFINE INPUT PARAMETER chTableName AS CHARACTER NO-UNDO.
FIND FIRST VALUE(chTableName) NO-LOCK
WHERE blahblahblah NO-ERROR.
IF AVAIL VALUE(chTableName) THEN
ASSIGN
VALUE(chTableName).value = foo.
END PROCEDURE.
This obviously does not work, but hopefully this will get the point across of what I'm trying to accomplish.
Any help or info in this matter would be appreciated. Thanks.
Hopefully you are running version 9 or better where dynamic queries are available.
procedure x:
define input parameter tbl as character no-undo.
define input parameter fld as character no-undo.
define input parameter xyz as character no-undo.
define variable qh as handle no-undo.
define variable bh as handle no-undo.
define variable fh as handle no-undo.
create buffer bh for table tbl.
create query qh.
qh:set-buffers( bh ).
qh:query-prepare( "for each " + tbl ).
qh:query-open.
do transaction:
qh:get-first( exclusive-lock ).
fh = bh:buffer-field( fld ).
display fh:buffer-value.
fh:buffer-value = xyz.
end.
delete object bh.
delete object qh.
return.
end.
run x ( "customer", "name", "fred" ).
find first customer no-lock.
display name.
This can get you started:
DEFINE TEMP-TABLE tt NO-UNDO
FIELD a AS INTEGER.
PROCEDURE foo:
DEFINE INPUT PARAMETER pcTable AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER pcWhere AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER pcField AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER piValue AS INTEGER NO-UNDO.
DEFINE VARIABLE hBuffer AS HANDLE NO-UNDO.
DEFINE VARIABLE hQuery AS HANDLE NO-UNDO.
CREATE BUFFER hBuffer FOR TABLE pcTable.
CREATE QUERY hQuery.
hQuery:SET-BUFFERS(hBuffer).
hQuery:QUERY-PREPARE("FOR EACH " + pcTable + " WHERE " + pcWhere).
hQuery:QUERY-OPEN().
hQuery:GET-FIRST().
IF hBuffer:AVAILABLE THEN DO:
ASSIGN
hBuffer:BUFFER-FIELD(pcField):BUFFER-VALUE = piValue NO-ERROR.
IF ERROR-STATUS:ERROR THEN DO:
MESSAGE "Failed" VIEW-AS ALERT-BOX ERROR.
END.
END.
DELETE OBJECT hBuffer.
DELETE OBJECT hQuery.
END PROCEDURE.
CREATE tt.
ASSIGN tt.a = 1.
RUN foo( INPUT "tt"
, INPUT "tt.a = 1"
, INPUT "a"
, INPUT 2).
DISPLAY tt.