UDF return error weirdness

smapdi636

New Member
Can anyone explain why in the following piece of code, the message statement ("my my-int function DOES NOT behave as I'd expect") is executed?

Code:
def var v-char as char no-undo.
def var v-int as int no-undo.

function my-int returns int (input v-char as char):
  def var v-int as int no-undo.
  v-int = int(v-char) no-error.
  if error-status:error then
	return error.
  else
	return v-int.	
end function. /* my-int */

/* using Progress' function */
v-char = "a".
v-int = int(v-char) no-error.
if error-status:error then
  message "progress' int function behaves as I'd expect" view-as alert-box.
  
/* using my function - why doesn't it behave the same??? */
v-char = "a".
v-int = my-int(v-char) no-error.
if error-status:error then
  message "my my-int function behaves as I'd expect" view-as alert-box.
else
  message "my my-int function DOES NOT behave as I'd expect" view-as alert-box.
 

Casper

ProgressTalk.com Moderator
Staff member
PHP:
Hi,


You can use the ERROR option only in a procedure or a database trigger block.
So you have to give the function a different return value if the error-status is raised.
Code:
function is-integer returns logical (input v-char as character):
define variable i_int as integer.
 
assign i_int = integer(v-char) no-error.
return not error-status:error.
 
end function.

Casper.
 

smapdi636

New Member
I'm still not seeing a way to make a UDF behave like Progress' built-in 4GL functions with regard to trapping errors.

What you suggest would suffice for sequential calls to the UDF, each followed by error-checking. However, if you have multiple calls to the UDF in an assign statement (for example) the code will not work.

Here's some modified code to demonstrate what I'm talking about. I understand why this doesn't work the way I'm hoping it would. Can anyone suggest a way to make it work so that, from an error-trapping perspective, the UDF would behave the same as the built-in 4GL function?

Code:
def var v-int as int no-undo.
def var v-err as log no-undo.
 
function my-int returns int (input v-char as char, output v-err as log):
  def var v-int as int no-undo.
  v-int = int(v-char) no-error.
  if error-status:error then do:
	v-err = true.
	return ?.
  end.
  else
	return v-int.
end function. /* my-int */
 
/* using Progress' function */
assign
v-int = int("a")
v-int = int("1") no-error.
if error-status:error then
  message "progress' int function behaves as I'd expect" view-as alert-box.
 
/* using my function - why doesn't it behave the same??? */
assign
v-int = my-int("a", v-err)
v-int = my-int("1", v-err).
if v-err then
  message "my my-int function behaves as I'd expect" view-as alert-box.
else
  message "my my-int function DOES NOT behave as I'd expect" view-as alert-box.
 

smapdi636

New Member
This KBase entry looks like it has a workaround. Gonna do some testing now.

ID: P100609
Title: "How to handle errors generated within Functions."

Also related:

ID: P100608
Title: "RETURN ERROR doesn't work when run from a Function."

and

Enhancement Request# 20050130-004
 

smapdi636

New Member
It seems Progress' published workaround fails as soon as you put multiple assigns on an assign statement.

Progress workaround:

Code:
/* Function foo raises a STOP if "a"
   cannot be converted to INT */
FUNCTION foo RETURNS INTEGER ( a AS CHAR ) :
   DEFINE VARIABLE i AS INTEGER	NO-UNDO.
   i = INTEGER(a) NO-ERROR.
   IF ERROR-STATUS:ERROR THEN STOP.
   RETURN i.   /* Function return value. */
END FUNCTION.


DO ON STOP UNDO, RETRY:
	IF RETRY THEN DO:
		MESSAGE 'A stop occurred within the function'
			VIEW-AS ALERT-BOX INFO BUTTONS OK.
		LEAVE.
	END.
	/* foo() will raise a STOP on appServer
	   if "a" cannot be converted to INT */
	DEFINE VARIABLE i AS INTEGER	NO-UNDO.
	i =  DYNAMIC-FUNCTION('foo':U, 'a').
	MESSAGE i VIEW-AS ALERT-BOX INFO BUTTONS OK.
END. /* ON STOP */

Failure scenario:

Code:
/* Function foo raises a STOP if "a"
   cannot be converted to INT */
FUNCTION foo RETURNS INTEGER ( a AS CHAR ) :
   DEFINE VARIABLE i AS INTEGER	NO-UNDO.
   i = INTEGER(a) NO-ERROR.
   IF ERROR-STATUS:ERROR THEN STOP.
   RETURN i.   /* Function return value. */
END FUNCTION.


DO ON STOP UNDO, RETRY:
	IF RETRY THEN DO:
		MESSAGE 'A stop occurred within the function'
			VIEW-AS ALERT-BOX INFO BUTTONS OK.
		LEAVE.
	END.
	/* foo() will raise a STOP on appServer
	   if "a" cannot be converted to INT */
	DEFINE VARIABLE i AS INTEGER	NO-UNDO.
	assign
	i =  DYNAMIC-FUNCTION('foo':U, 'a')
	i =  DYNAMIC-FUNCTION('foo':U, '1').
	MESSAGE i VIEW-AS ALERT-BOX INFO BUTTONS OK.
END. /* ON STOP */
 

Casper

ProgressTalk.com Moderator
Staff member
Hi,

I see your point. I can't think of a good solution for your problem, other then use other ways, like checking before the assign.
I mean something like:

Code:
if f_checkmy-int('a') and
f_checkmy-int('1')
then assign somevar1 = my-int('a')
	 somevar2 = my-int('1').
else do:
/* stuff you want to do if check fails*/
end.

But this surely isn't what you're looking for either, I suppose...

You sure gave me something to think about today. :)

If you find a way, please let me know.

Casper.
 

smapdi636

New Member
I think I see what you're saying, and that does seem to get a little closer to the approach I'm looking for. I'd probably modify my example like so:

Code:
def var v-int as int no-undo.

function my-int returns int (input v-char as char):
  def var v-int as int no-undo.
  v-int = int(v-char) no-error.
  if error-status:error then
	return ?.
  else	
	return v-int.
end function. /* my-int */

/* using Progress' function */
assign
v-int = int("a")
v-int = int("1")
no-error.
if error-status:error then
	message "Progress' function behaves as I'd expect" view-as alert-box.

/* using my function - why doesn't it behave the same? */
assign
v-int = if my-int("a") <> ? then my-int("a") else int("a") /* force error */
v-int = if my-int("1") <> ? then my-int("1") else int("a") /* force error */
no-error.
if error-status:error then
	message "My function behaves as I'd expect" view-as alert-box.

BUT, if the function had some database access in it, there still isn't a way to ensure the first call to the UDF would return the same result as the second call to the UDF. In other words:

Code:
v-int = if my-int("a") <> ? /* does not return ? this time */
then 
	my-int("a") /* db field changed, now returns ?; v-int has invalid value */
else
	int("a") /* force error */
 

jongpau

Member
Nasty nasty nasty....

I never encountered this problem myself (guess I was lucky), but I see where you are coming from. I tried several approaches, including introducing an error variable that was set to true and checked with a "when" in the assign, but the change of the variable is also not detected within a single assign statement.

Hmmm definately one to think about, but I am not too optimistic about finding a solution. If anyone does I sure am interested as well.
 
Top