Extracting the "nullth" attribute from a dynamic array!

234 views
Skip to first unread message

Brian Speirs

unread,
Nov 9, 2016, 7:46:51 PM11/9/16
to Pick and MultiValue Databases
Hi everyone,

Here is something that may amuse some of you... I'm sure some of you will say, "Well, you should have checked that a bit better", or words to that effect. However, I hope this does warn some of you that statements may not work as you think!

We had a line in one of our programs that read something like (and NO - I wasn't the one who wrote the line):

  ANS = REC2<REC1<AMC>, 2>

That mostly worked fine. It obviously worked under the conditions it was tested in - but not all conditions were tested for.

The problem is when REC1<AMC> evaluates to a null string. I've looked at that line many times, and in doing so, I have known that REC1<AMC> would (sometimes) evaluate to null. I always assumed that ANS would evaluate to null in those instances where REC1<AMC> was null. How wrong I was!

Here is a little program to try:

PROGRAM EXTRACT.TEST
amc = 22
rec1 = ''
rec2 = CONVERT('^]', @AM:@VM, '5559]2396^6440]1288^12821]2424^9999999999]2424')
ans = rec2<rec1<amc>, 2>
CRT 'Ans: ':ans
STOP
END

In UniVerse 11.2.4 / Windows, 'ans' evaluates to 2396. This means that rec2<'', 2> is evaluated as rec2<1, 2>.

The UniVerse documentation says (under the EXTRACT function):

"If a higher-level delimiter expression has a value of 0 when a lower-level delimiter is greater than 0, a 1 is assumed..."

So, in this context, UniVerse is clearly evaluating the null string as zero. I wouldn't have thought that was correct - but the words are ambiguous. It can well be argued that the null string has a VALUE of zero, even though it isn't actually zero.

Out of curiosity, I tried the above program in OpenQM. In that case, 'ans' evaluated to a null value (or empty string). In my view, that is a much better output than the one returned by UniVerse.

You can choose your own moral for this story. Needless to say, we are now checking the value of REC1<AMC> before evaluating ANS!

Cheers,

Brian

Ed Clark

unread,
Nov 9, 2016, 11:22:16 PM11/9/16
to mvd...@googlegroups.com
IIRC, in most contexts universe will use 0 when it needs a numeric value and gets a non-numeric one. It usually prints a message like “Nonnumeric data when numeric required.  Zero used”, but in this case it knows to treat “” as 0, which becomes 1. Note that if rec1<amc> was some non-blank non-numeric value, it would print the error message, and use 0, which the extract function would still treat as 1.

I wouldn’t say that OpenQM was better or worse—just different, and potentially a problem in a migration, because you just *know* that there is code out there expecting nothing to be 1 in an extraction.

Compare to something like
  V=“”
  READV val FROM FP,ID,V ELSE ABORT
On universe this empty V also gets treated as 0 and reads the first value of item ID, but on unidata when it gets treated as 0 it returns true or false whether the item exists. These little emulation details can be a lot of fun.


--
You received this message because you are subscribed to
the "Pick and MultiValue Databases" group.
To post, email to: mvd...@googlegroups.com
To unsubscribe, email to: mvdbms+un...@googlegroups.com
For more options, visit http://groups.google.com/group/mvdbms

David A. Green

unread,
Nov 10, 2016, 9:20:37 AM11/10/16
to mvd...@googlegroups.com

This brings up memories when I was first learning to program.  One of the main principles was to never assume what the computer was going to do under unknown circumstances.  Take for instance a for/next loop.  Given this code:

 

FOR I=1 TO 10

    PRINT I

NEXT I

PRINT I

 

Some systems will print 10 some print 11.

 

David A. Green

(480) 201-7953

DAG Consulting

--

Dave Humphreys

unread,
Nov 10, 2016, 9:20:39 AM11/10/16
to Pick and MultiValue Databases

Not directly related but I hope someone may have an answer - running Universe 11.2 . 

In programs where where are working out %ages ( usually margins)  we typically have (old) code that reads   ANSWER = SALES / TARGET. If TARGET is 0 then this creates a divide by zero message in the ERRLOG.which is then reported to us by our customer . Without changing all the programs to read IF TARGET = 0 THEN ANSWER = 0 ELSE ANSWER = SALES / TARGET is there a config setting or any other way to prevent an ERRLOG  message being written.

Regards

Dave


Tony Gravagno

unread,
Nov 10, 2016, 10:49:35 AM11/10/16
to Pick and MultiValue Databases
Sometimes we code to the reasonable nuance, like the knowledge that 0=false and anything else is true, or that in MV an empty string Can Sometimes be acceptable as a default of zero.

In Dave's specific business case the program logic really should provide filtering, data validation, etc.That error message could be a legitimate warning that something is wrong. Suppressing it is probably not a good idea. Once in a while we see reports in forums (MV and otherwise) that a developer suppressed an important bit of diagnostic information and it resulted in some kind of business turmoil. It's better to handle the condition - if nothing else than to verify for the next developer the actual intent of the code.

Thinking aloud...

It's a shame that MVBASIC syntax doesn't evolved like it does elsewhere where this might be re-written as:
ANSWER = TARGET=0 ? 0 : (SALES/TARGET)

I could easily see (our hero) Martin doing this in QM, but chances are slim anywhere else.
In a language that supports functions (Universe, QM, mvBASE...) you could do something like this:
ANSWER = DBZ(SALES,TARGET) ; * Divide By Zero handling
Where:
DEFINE FUNC DBZ(NUMERATOR,DENOMINATOR)
  IF DENOMINATOR+0 = 0 THEN RETURN 0
  RETURN NUMERATOR / DENOMINATOR
END FUNC
Dave, you might want to go for that option...
And everyone has this option:
CALL DBZ(ANSWER,SALES,TARGET)

Sure, there is just as much re-writing there as with IF TARGET=0... but sometimes when you're doing the same kind of operation over and over it can be elegant to encapsulate the functionality just in case you want to modify it later.

Most non-MV environments also have Try/Catch, again to make explicit that you do expect an error and that you don't want to throw an error:
TRY
  ANSWER=SALES/TARGET
CATCH
  ANSWER=0
END TRY
Again, the only MVBASIC dialect I know that supports this is QM where there is a specific catch for SYS.PROGRAM.DIV_ZERO.

Or there might be some sort of compiler directive (made up example):
#SDNZ.ON // Suppress DNZ errors in this block
ANSWER=SALES/TARGET
#SDNZ.OFF
I don't remember if Universe has anything like that but QM has the DIV.ZERO.WARNING option for entire sessions or modules.

Oh if only larger companies with more staff could be as agile as our friend Martin.

Regards,
T
(Please put completely separate questions in different threads so they don't get lost.)

Martin Phillips

unread,
Nov 10, 2016, 11:08:45 AM11/10/16
to mvd...@googlegroups.com

Hi Tony,

 

It's a shame that MVBASIC syntax doesn't evolved like it does elsewhere where this might be re-written as:
ANSWER = TARGET=0 ? 0 : (SALES/TARGET)

I could easily see (our hero) Martin doing this in QM, but chances are slim anywhere else.

Your posting set me off looking at the D3 documentation and I was a bit surprised that the conditional expression syntax found in “Information style” multivalue implementations is not there. I thought it was part of the core language.

 

ANSWER = IF TARGET = 0 THEN 0 ELSE (SALES / TARGET)

 

So, No opportunity to be the hero this time. I will put the Superman cape back in the box for now.

 

 

Martin Phillips
Ladybridge Systems Ltd
17b Coldstream Lane, Hardingstone, Northampton NN4 6DB, England
+44 (0)1604-709200

 

 

Tony Gravagno

unread,
Nov 10, 2016, 11:28:34 AM11/10/16
to Pick and MultiValue Databases
Dave was using AP/D3 and might still be. But his current enquiry is for Universe. I'd guess he's using Pick flavour, but whether that or Information there might be an option in Universe.

My hero note was regarding support for the ?: operator:
RESULT = CONDITION ? TRUE.RESULT : FALSE.RESULT

Best,
T

Ian Harper

unread,
Nov 10, 2016, 12:36:09 PM11/10/16
to Pick and MultiValue Databases
I just ran a little test in D3 and found that:
x = 1:@AM:2:@AM:3
crt x
<0>
crt x
<2,0>

Outputs an empty string & 2. I've used 0 as the attribute count for inserts at the beginning of the array without issue. The D3 documentation makes this note:


An extract of attribute 0 is undefined and should be avoided.


Ed Clark

unread,
Nov 10, 2016, 1:06:56 PM11/10/16
to mvd...@googlegroups.com
Caché has try/catch in mvbasic with both well-defined system exceptions and user exceptions, but that probably doesn’t help anyone here.

You could write a more generalized deffun function
  FUNCTION CONDITIONAL(condition,trueValue,falseValue)
  IF condition THEN RETURN trueValue ELSE RETURN falseValue
but both your function and mine are dangerous because all the parameters will always be evaluated and might cause side effects. We would really need a built-in structure like your “?” operator, or a CASE() or SELECT() function 

I agree that it’s a shame that mvbasic syntax hasn’t evolved. I wonder if anyone at the mv platform vendors actually uses mvbasic on a regular basis. (except for Martin who obviously cares for his platform).

(OFFTRACK, Caché object script does have CASE() and SELECT() intrinsic functions, but also has a macro preprocessor that can do some amazing things like the DEFFUNs mentioned without worry about unwanted evaluations. )

chandru murthi

unread,
Nov 21, 2016, 10:52:09 AM11/21/16
to Pick and MultiValue Databases
I'm sure there's nobody who remembers the Ultimate Basic LET variation added (by me) in the last release before they folded:
LET X to Y THEN/ELSE...
which would take the ELSE clause if X were false (null or 0). Memory is fuzzy as to  why I thought this a good idea, but I think I wanted it for the (Ultimate) Update processor to replace numerous IF X THEN Y=X ELSE [...]

Chandru

Peter McMurray

unread,
Nov 21, 2016, 4:57:50 PM11/21/16
to Pick and MultiValue Databases
The disastrous decision of some designers to default maybe :-(
My rule is DON'T.
If the variable is undefined: throw an exception report or stop either in debug or TCL. Of course some clown would undoubtedly use the BASIC Sue me option which I am delighted to see has vanished from D3 which has the excellent LIST-RUNTIME-ERRORS.
If the variable is Null be it an empty string or the null character do not use Zero. 
In the meantime I shall have to continue with lines such as CASE QTY NE 0 AND QTY NE NOVAL as on some systems the zero test would work and on others it won't.



chandru murthi

unread,
Nov 21, 2016, 6:18:14 PM11/21/16
to Pick and MultiValue Databases
You can use CASE QTY MATCH '"0"' which will work on every variation. Unless, of course, QTY has some variant of 00 0000  00.0000 etc ;)

Peter McMurray

unread,
Nov 22, 2016, 2:42:34 PM11/22/16
to Pick and MultiValue Databases
Hi Chandru
"You can use CASE QTY MATCH '"0"' which will work on every variation. Unless, of course, QTY has some variant of 00 0000  00.0000 etc ;)" 
Exactly! the quantities of liquids such as fuel are always kept to at least two decimal places and can of course be negative for credits.
On the question of decimal places our "brilliant" tax department decreed that GST MUST be calculated to six decimal places on the PRICE which for fuel was already four decimal places. Then clown lawyers drew up the legislation and failed to define the difference between PRICE and VALUE so invoices can have GST variations depending on whose software calculates it.
My software was checked by the ACCC because at the time most of the fuel in the state was delivered using my systems and we were congratulated on being one of the few with the calculation correct. This did not stop the occasional customer of my clients' saying it was wrong because their software came up with a different answer :-(


chandru murthi

unread,
Nov 22, 2016, 9:05:24 PM11/22/16
to Pick and MultiValue Databases

I was joking, but you're serious and I can't see how. How do you creaee a "-0.0" unless it's string math or something. I mean, BASIC will always create the Numeral ZERO, period, if you do math that results in 0, so the Case statement will work. I believe this is true evn if it internally needs string or FP math. So how?

And if it does not because of your string zero, your CASE won't work either.

C.

Wols Lists

unread,
Nov 24, 2016, 12:03:14 PM11/24/16
to mvd...@googlegroups.com
On 23/11/16 02:05, chandru murthi wrote:
>
> I was joking, but you're serious and I can't see how. How do you creaee
> a "-0.0" unless it's string math or something. I mean, BASIC will always
> create the Numeral ZERO, period, if you do math that results in 0, so
> the Case statement will work. I believe this is true evn if it
> internally needs string or FP math. So how?

OCONV( -0.001, "MD1P") ???

Just because your system is sensible, doesn't mean other systems are.
All you've got to do is store the number in external format and you
won't get a plain 0. Whether basic sticks the - before 0, I don't know,
but I've met plenty of systems that do ...
>
> And if it does not because of your string zero, your CASE won't work either.
>
> C.
>
Cheers,
Wol

chandru murthi

unread,
Nov 24, 2016, 12:41:45 PM11/24/16
to Pick and MultiValue Databases

Well, yeah, a big maybe there as thet's hardly standard...ie where did the -0.01 come from in the first place?

And, if you don't add +0 to extracted numbers (ie  QTY= RECORD<11,I>+0, it's all your damn fault!

Peter McMurray

unread,
Nov 24, 2016, 2:41:39 PM11/24/16
to Pick and MultiValue Databases
HI Chandru
I have found a couple of simple rules when working across multiple platforms:
1 If it works don't fix it - sorry Bruce RIP :-)
2 Never assume anything.
 Classic example in D3 BASIC prog and COMPILE PROG (O) did not produce the same result with "MR0" when presented with the string SPACE(8):"12345678". This was a credit card number from a Caltex csv download. For many years the statement had trimmed the spaces thus chaos ensued when the optimise option did not. I do not disagree with the interpretation by the persons who wrote the Optimise code but one can get caught in the most innocent of circumstances. 
Adding zero will not work if it is presented with a non numeric character such as space, it will produce a runtime error.
 

Charlie Noah

unread,
Nov 24, 2016, 5:41:40 PM11/24/16
to mvd...@googlegroups.com
Hey Peter,

I agree with both with an addendum to #1: Just because it works today doesn't mean it will work forever.

Of course, I'm sure you know that one should probably not treat a credit card number, SKU, UPC or other like data as numeric, even though they consist of numeric digits only. They may have meaningful leading zeroes or may contain alpha or symbols later.

I don't know a lot about D3 compile options, so I can't really speak to that. It seems to me that an optimize option (whatever it does) shouldn't change the code functionality. Just my 2¢ worth. Correct me if I'm wrong here.

Charlie
--

Peter McMurray

unread,
Nov 25, 2016, 4:12:53 PM11/25/16
to Pick and MultiValue Databases

Hi
Regarding credit card numbers people may not be aware that there was no standardisation when we began controlling petroleum systems back in 1983. In fact the first 8 characters could be used any way the seller chose. Gilbarco chose to use them to identify groups of dealers for example whereas Caltex chose to show them as spaces. This had some interesting results when our major client using Gilbarco, Mobil in the North of the state, took over the group of dealers in the south of the state. We coped quite nicely. I have actually programmed a significant chunk of the current international system for the Trust Bank when it was taken over by the Commonwealth bank in the late '90s and I can assure you their are no non numerics in the current system. Remember that internal systems such as Caltex Starcard are not valid outside the specific organisation. It might look lie a duck and quack like a duck but it is only a duck in a specific pond :-). Genuine Bank Credit card numbers are not SKUs and they are subject to an international standard now.

The Compile optimise issue arises because in the D3 system it is actually completely separate code to the original Basic code and really the function should only receive numbers. In fact it returns the numbers still prefixed by spaces which I feel is an error also. Correction is easily achieved by a TRIM before the ICONV but when the previous method had worked for 30 years I believe that we could be forgiven for not noticing an issue that the D3 engineers did not know existed until we told them.

chandru murthi

unread,
Nov 27, 2016, 6:27:23 PM11/27/16
to Pick and MultiValue Databases
Good point, I suppose if one looks at data from enough sources, there could easily be '00.00' or whatever. I forgot this obvious source of error, I was focusing on data one has control of. Or has massaged...
Reply all
Reply to author
Forward
0 new messages