Use cases in operations in VDM++ - case-statement

Im pretty new to VDM++ and im trying to use cases in a operation to see how it works.
My idea was to give the operation an input and see what it gave me as output. So fx. my input could be: and I would expect output to be Even.
The following operations fails and give me the error "Unexpected token in expression"
public sign: (seq of char) ==> (seq of char)
sign(sign) ==
cases sign:
<Even> -> "Even",
<Odd> -> "Odd",
others -> "Unknown"
end

Hopefully the error is actually Unexpected token in statement (not expression?). The cases statement requires the RHS of each case clause to be a statement. So you would have to say -> return "Even". Alternatively, you can say return cases sign: ..., turning it into one large return statement, where the cases expression that followed it would then be correctly formed.

Related

Can't declare a variable in ArangoDB

I'm using ArangoDB 3.4.6-1 and would like to delete vertices with AQL (as stated here) in online console.
In the first step according to the tutorial you are supposed to save your Edges into a variable. In my case the statement looks like this:
LET edgeKeys = (FOR v, e, p IN 1..100 INBOUND 'Node/N3' GRAPH 'graph' RETURN e._key)
The For itself without the brackets returns the correct result:
[
"E3"
]
Yet, running the whole statement with the brackets just throws the following error:
Query: AQL: syntax error, unexpected end of query string near ')' at position 1:83 (while parsing)
I tried using a comparable command with other graphs or other returned values and objects, but always get the same error.
So far I wasn't able to find a proper solution online. The tutorial provides the following example code (copied 1:1):
LET edgeKeys = (FOR v, e IN 1..1 ANY 'persons/eve' GRAPH 'knows_graph' RETURN e._key)
And I'm getting exactly the same error, it's not even able to check the collections.
What am I doing wrong?
Only defining a variable with LET is not a valid AQL statement.
From the AQL Syntax documentation:
An AQL query must either return a result (indicated by usage of the
RETURN keyword) or execute a data-modification operation (indicated by
usage of one of the keywords INSERT, UPDATE, REPLACE, REMOVE or
UPSERT). The AQL parser will return an error if it detects more than
one data-modification operation in the same query or if it cannot
figure out if the query is meant to be a data retrieval or a
modification operation.
Using the full AQL block that is stated in the tutorial the execution works as expected since the query is executing a data-modification with REMOVE in this case. Just a RETURN operation inside the LET variable declaration is not sufficient to run an AQL query. When removing the LET operation the query works as well since in this case the AQL query directly returns the result.
Complete AQL query:
LET edgeKeys = (FOR v, e IN 1..1 ANY 'persons/eve' GRAPH 'knows_graph' RETURN e._key)
LET r = (FOR key IN edgeKeys REMOVE key IN knows)
REMOVE 'eve' IN persons
An additional RETURN also makes the query work:
LET edgeKeys = (FOR v, e IN 1..1 ANY 'persons/eve' GRAPH 'knows_graph' RETURN e._key)
RETURN edgeKeys

Function which takes a string as parameter

In kdb I would like to have a function which takes a string as a parameter.
myfunc: {[strParam]
....
}
However when I tried to invoke the function.
q) myfunc["test"]
It complains of length error. It seems that function sees "test" as passing 4 char parameters. How can I make the function expect a string?
A string in kdb is defined as a list of characters and so functions using them have to be able to deal with this.
q)count "test"
4
You can also use a symbol instead casting from a string using `symbol$"test". A symbol is atomic and fixed width so can be useful to use as keys to a dictionary or in a table. Some functions for strings will still work on symbols e.g
q)upper `test
`TEST
while list operation will not work and you will have to turn it back into a string using string `test before using those operations.
When a length error is thrown and you go into the debug mode as shown by the q prompt having two brackets q)), you can use the functions .z.ex to show the failed function and .z.ey to see the failed arguments to narrow down which part is throwing the error.
The error can appear due to multiple reasons.
q)f:{[p] show p} //it works fine with any number of elements
q)f["abc"]
"abc"
f:{[p] enlist[`k]!p} //needs a list of single element
f["abc"]
'length
f[enlist "abc"]
(enlist `k)!enlist "abc"
q)f:{[p] 1 2 3 4!p} //the length of values is different from the length of keys
q)f["abc"]
'length
q)f["abcd"]
(1j, 2j, 3j, 4j)!"abcd"

Invalid length parameter passed to the LEFT or SUBSTRING function UNION ALL

I have two queries that use the SUBSTRING function within a CASE statement like so:
CASE
WHEN Answer.ChoiceTitle = 'Neither Likely or Unlikely'
THEN 'Neither Likely nor Unlikely'
WHEN Answer.ChoiceTitle LIKE '[1-5]%'
THEN SUBSTRING(Answer.ChoiceTitle, 3, LEN(Answer.ChoiceTitle) - 2)
ELSE Answer.ChoiceTitle
END AS Recommendation
Both queries run perfectly fine when run separately but when I try to combine both result sets with a UNION ALL I get the error message:
Invalid length parameter passed to the LEFT or SUBSTRING function
Whilst trying to figure out why this error is occurring I added the below to each statement and the UNION ALL now works perfectly fine.
MIN(LEN(Answer.ChoiceTitle)) OVER() AS MinLength
Why would I be getting this error?
Execution Plans
Planned Execution Plan with UNION ALL - https://www.brentozar.com/pastetheplan/?id=rksFnuLS-
Actual Execution Plan of first statement - https://www.brentozar.com/pastetheplan/?id=r1Z-pO8HW
Actual Execution Plan of second statement - https://www.brentozar.com/pastetheplan/?id=rkCTh_IBb
This is most likely causing your error: LEN(Answer.ChoiceTitle) - 2
When that evaluates to less than 0, it will throw an error.
Try this instead:
CASE
WHEN Answer.ChoiceTitle = 'Neither Likely or Unlikely'
THEN 'Neither Likely nor Unlikely'
WHEN Answer.ChoiceTitle LIKE '[1-5]%' and LEN(Answer.ChoiceTitle) > 2
THEN SUBSTRING(Answer.ChoiceTitle, 3, LEN(Answer.ChoiceTitle) - 2)
ELSE Answer.ChoiceTitle
END AS Recommendation
Since you are just getting rid of the first two characters, you could use stuff() instead like so:
CASE
WHEN Answer.ChoiceTitle = 'Neither Likely or Unlikely'
THEN 'Neither Likely nor Unlikely'
WHEN Answer.ChoiceTitle LIKE '[1-5]%'
THEN stuff(Answer.ChoiceTitle,1,2,'')
ELSE Answer.ChoiceTitle
END AS Recommendation
This will give you an empty string if the length is less than 3, otherwise it will remove the first two characters of Answer.ChoiceTitle.
As to why the combined query with union all throws an error when the others run alone do not:
I'm seeing this difference in the execution plans:
Hash Match > (Question & Survey nested loop) & (Compute Scalar > Answer) {Bottom right of execution plan without error}
vs
Hash Match > (Bitmap > Parallelism > Question) & (Compute Scalar > Answer) {Bottom right of execution plan with error}
the nested loop version may be filtering rows that cause the error prior to the hash match, thus avoiding the error.
It is possible that using option (maxdop 1) to prevent parallelism would avoid the error as well (confirmed) on the query that is currently throwing it
this just comes down to when the scalar function is being evaluated for the rows in the answer table, pre or post filtering out the rows you want to run the expression on.
The cost is higher in the union all version, and it exceeds the cost threshold for parallelism, which is why you wouldn't see the same error when run alone where it does not go parallel (specifically parallel in the same way) with the lower cost when run alone.
So basically the parallel plan is running the substring() sooner than your other plans, before the rows that throw errors are filtered out.

Perl error: use of uninitialized value $DBI::err in concatenation

I wrote a procedure which imports data from an xml-file into a MariaDB using library DBI. The procedure works but I don't understand why the following code gives me the message:
use of uninitialized value $DBI::err in concatenation (.) or string at ...
Here the code (abbreviated):
my $insert_art = $dbh->prepare(
"INSERT INTO odbb_articles (creation_dt,ref_data,comp_no)".
"VALUES (?,?,?)"
);
....
my $comp_no = $xpc->findvalue('./sr:ARTCOMP/sr:COMPNO',$node1);
....
$insert_art->execute($creation_dt,$ref_data,$comp_no)
or die "Fehler bei der Ausfuehrung: ".
"$DBI::err -> $DBI::errstr (odbb_articles $DBI::state)\n";
If I insert the code
if ($comp_no eq "") { $comp_no = undef; }
just before $insert_art->execute the procedure works. This error happens when there is no entry in the xml-file for element COMPNO. I can avoid it if I define it as undef. I just wonder
why $comp_no cause this problem and
is there another solution than to control if $comp_no is "" and define it as undef?
The reason for the second question is to avoid the if statement if there are a lot of variables/columns which may have empty entries.
Thanks for help.
use of uninitialized value $DBI::err in concatenation (.) or string at ...
The error message you are seeing is Perl telling you that $DBI::err is undef. That is not because of the value of your $comp_no. It's just a result of what your program is doing.
So when you pass an empty string to the comp_no column, the database doesn't like that. It throws an error. DBI catches that error and passes it on. The $insert_art->execute returns a false value and the right-hand-side of the or gets called. That's your die.
Now in the string that you pass to die you put three variables:
$DBI::err
$DBI::errstr
$DBI::state
According to the DBI documentation, those are equivalent to the functions $h->err, $h->errstr and $h->state with $h being the last handle used. Let's look at the docs for those.
$h->err
Returns the native database engine error code from the last driver method called. The code is typically an integer but you should not assume that.
The DBI resets $h->err to undef before almost all DBI method calls, so the value only has a short lifespan. Also, for most drivers, the statement handles share the same error variable as the parent database handle, so calling a method on one handle may reset the error on the related handles. [...]
This does not explain when it can be undef.
$h->errstr
Returns the native database engine error message from the last DBI method called. This has the same lifespan issues as the "err" method described above.
The returned string may contain multiple messages separated by newline characters.
The errstr() method should not be used to test for errors, use err() for that, because drivers may return 'success with information' or warning messages via errstr() for methods that have not 'failed'.
Ok, so this is text. Don't use it to test for specific errors. You're not doing that. You just want to give debug output when the program fails.
$h->state
Returns a state code in the standard SQLSTATE five character format. Note that the specific success code 00000 is translated to any empty string (false). If the driver does not support SQLSTATE (and most don't), then state() will return S1000 (General Error) for all errors.
The driver is free to return any value via state, e.g., warning codes, even if it has not declared an error by returning a true value via the "err" method described above.
The state() method should not be used to test for errors, use err() for that, because drivers may return a 'success with information' or warning state code via state() for methods that have not 'failed'.
Again, this is not very clear about how useful it is.
My advice is to get rid of $DBI::err and $DBI::state. You don't need those to figure out what the problem is. Just output $DBI::errstr.
$insert_art->execute($creation_dt,$ref_data,$comp_no)
or die "Fehler bei der Ausfuehrung: " . $dbh->errstr;
Now your program will still fail, but at least you will have a meaningful error message that will explain what your database didn't like about the statement. That's better than being told how there is a bug in your error handling code.
Afterwards, the other answers will probably apply to fix the reason this is happening in the first case.
On another note, a word on die: If you provide a \n at the end of your arguments, it will not print your current script, line number and input handle line number. But those might be useful in your case. You can include them.
In a SQL database an empty string is very different to null.
If comp_no has a foreign key pointing to a record in another table, then the value "" is an accettable one only if there is a record with "" as primary key, very improbable.
Yu can fix this converting empty values to undef:
for ($creation_dt,$ref_data,$comp_no ){
defined $_ and $_ eq '' and $_ = undef;
}
$insert_art->execute($creation_dt,$ref_data,$comp_no);
or also
$insert_art->execute(map {defined($_) && length($_) ? $_ : undef} ($creation_dt,$ref_data,$comp_no));
This is a possible shortcut:
$comp_no ||= undef;
With the caveat that this will work in any case where $comp_no evaluates as false, meaning a value of 0 will actually cause the result to go undef also, which may or may not matter to you. If your field is numeric, I'd say it matters a lot.

Erlang mnesia equivalent of "select * from Tb"

I'm a total erlang noob and I just want to see what's in a particular table I have. I want to just "select *" from a particular table to start with. The examples I'm seeing, such as the official documentation, all have column restrictions which I don't really want. I don't really know how to form the MatchHead or Guard to match anything (aka "*").
A very simple primer on how to just get everything out of a table would be very appreciated!
For example, you can use qlc:
F = fun() ->
Q = qlc:q([R || R <- mnesia:table(foo)]),
qlc:e(Q)
end,
mnesia:transaction(F).
The simplest way to do it is probably mnesia:dirty_match_object:
mnesia:dirty_match_object(foo, #foo{_ = '_'}).
That is, match everything in the table foo that is a foo record, regardless of the values of the fields (every field is '_', i.e. wildcard). Note that since it uses record construction syntax, it will only work in a module where you have included the record definition, or in the shell after evaluating rr(my_module) to make the record definition available.
(I expected mnesia:dirty_match_object(foo, '_') to work, but that fails with a bad_type error.)
To do it with select, call it like this:
mnesia:dirty_select(foo, [{'_', [], ['$_']}]).
Here, MatchHead is _, i.e. match anything. The guards are [], an empty list, i.e. no extra limitations. The result spec is ['$_'], i.e. return the entire record. For more information about match specs, see the match specifications chapter of the ERTS user guide.
If an expression is too deep and gets printed with ... in the shell, you can ask the shell to print the entire thing by evaluating rp(EXPRESSION). EXPRESSION can either be the function call once again, or v(-1) for the value returned by the previous expression, or v(42) for the value returned by the expression preceded by the shell prompt 42>.