Are Ada function arguments evaluated if the body is empty? - compiler-optimization

According to this statement :
Trace.Debug("My String" & Integer'Image(x) & "is evaluated" & "or not" & "if my logger is disabled ?" & Boolean'Image(YesOrNo) );
And this implementation of Trace.Debug:
procedure Debug (Message : in String) is
begin
if Logger.Enabled then -- This boolean is defined during runtime by reading a value in a file
Put_Line(Message);
else
null; -- Do nothing
end if;
end Debug;
I have a software which can manage several levels of logs, and I would like to know what is the behavior in case of Logger.Enabled equals False.
I have a lot of logs calls, with sometimes complex strings to evaluate and I'm on a real time system, so I don't want to lost time to evaluate a string which will not printed.
I would like to know if compiler optimize the code in order to not evaluate the string in parameter of Trace.Debug while Logger.enabled is False, knowing that this boolean is set at the begging of runtime by reading a value in a file.
I am using gnat 7.3.2.

You can ensure that evaluation doesn't happen by providing a callback:
procedure Debug (Message : access function return String) is
begin
if Logger.Enabled then
Put_Line(Message.all);
end if;
end Debug;
Then to call it, do
declare
function Msg return String is
("My String" & Integer'Image(x) & "is evaluated" & "or not" & "if my logger is disabled ?" & Boolean'Image(YesOrNo));
begin
Debug (Msg'Access);
end;
In your original code, the only way that the compiler could skip the evaluation is when it inlines the Debug procedure and re-arranges the code so that the Message object is only assigned inside the if-block. You cannot force this; even pragma Inline is only a hint for the compiler.

Related

Postgres: Returning Results or Error from Stored Functions

I am struggling to figure out how to best handle the return of results or errors to my application from Postgres stored functions.
Consider the following contrived psudeocode example:
app.get_resource(_username text)
RETURNS <???>
BEGIN
IF ([ ..user exists.. ] = FALSE) THEN
RETURN 'ERR_USER_NOT_FOUND';
END IF;
IF ([ ..user has permission.. ] = FALSE) THEN
RETURN 'ERR_NO_PERMISSION';
END IF;
-- Return the full user object.
RETURN QUERY( SELECT 1
FROM app.resources
WHERE app.resources.owner = _username);
END
The function can fail with a specific error or succeed and return 0 or more resources.
At first I tried creating a custom type to always use as a standard return type in eachh function:
CREATE TYPE app.appresult AS (
success boolean,
error text,
result anyelement
);
Postgres does not allow this however:
[42P16] ERROR: column "result" has pseudo-type anyelement
I then discovered OUT parameters and attempted the following uses:
CREATE OR REPLACE FUNCTION app.get_resource(
IN _username text,
OUT _result app.appresult -- Custom type
-- {success bool, error text}
)
RETURNS SETOF record
AS
$$
BEGIN
IF 1 = 1 THEN -- just a test
_result.success = false;
_result.error = 'ERROR_ERROR';
RETURN NULL;
END IF;
RETURN QUERY(SELECT * FROM app.resources);
END;
$$
LANGUAGE 'plpgsql' VOLATILE;
Postgres doesn't like this either:
[42P13] ERROR: function result type must be app.appresult because of OUT parameters
Also tried a similar function but reversed: Returning a custom app.appresult object and setting the OUT param to "SETOF RECORD". This was also not allowed.
Lastly i looked into Postgres exception handling using
RAISE EXCEPTION 'ERR_MY_ERROR';
So in the example function, i'd just raise this error and return.
This resulted in the driver sending back the error as:
"ERROR: ERR_MY_ERROR\nCONTEXT: PL/pgSQL function app.test(text) line 6 at RAISE\n(P0001)"
This is easy enough to parse but doing things this way feels wrong.
What is the best way to solve this problem?
Is it possible to have a custom AppResult object that i could return?
Something like:
{ success bool, error text, result <whatever type> }
//Edit 1 //
I think I'm leaning more towards #Laurenz Albe solution.
My main goal is simple: Call a stored procedure which can return either an error or some data.
Using RAISE seems to accomplish this and the C++ driver allows easy checking for an error condition returned from a query.
if ([error code returned from the query] == 90100)
{
// 1. Parse out my overly verbose error from the raw driver
// error string.
// 2. Handle the error.
}
I'm also wondering about using custom SQLSTATE codes instead of parsing the driver string.
Throwing '__404' might mean that during the course of my SPs execution, it could not continue because some record needed was not found.
When calling the sql function from my app, i have a general idea of what it failing with a '__404' would mean and how to handle it. This avoids the additional step of parsing driver error string.
I can also see the potential of this being a bad idea.
Bedtime reading:
https://www.postgresql.org/docs/current/static/errcodes-appendix.html
This is slightly opinion based, but I think that throwing an error is the best and most elegant solution. That is what errors are for!
To distinguish various error messages, you could use SQLSTATEs that start with 6, 8 or 9 (these are not used), then you don't have to depend on the wording of the error message.
You can raise such an error with
RAISE EXCEPTION SQLSTATE '90001' USING MESSAGE = 'my own error';
We do something similar to what you're trying to do, but we use TEXT rather than ANYELEMENT, because (almost?) any type can be cast to TEXT and back. So our type looks something like:
(errors our_error_type[], result TEXT)
The function which returns this stores errors in the errors array (it's just some custom error type), and can store the result (cast to text) in the result field.
The calling function knows what type it expects, so it can first check the errors array to see if any errors were returned, and if not it can cast the result value to the expected return type.
As a general observation, I think exceptions are more elegant (possibly because I come from a c# background). The only problem is in plpgsql exception handling is (relatively) slow, so it depends on the context - if you're running something many times in a loop, I would prefer a solution that doesn't use exception handling; if it's a single call, and/or especially when you want it to abort, I prefer raising an exception. In practice we use both at various points throughout our call stacks.
And as Laurenz Albe pointed out, you're not meant to "parse" exceptions, so much as raise an exception with specific values in specific fields, which the function that catches the exception can then extract and act on directly.
As an example:
Setup:
CREATE TABLE my_table (id INTEGER, txt TEXT);
INSERT INTO my_table VALUES (1,'blah');
CREATE TYPE my_type AS (result TEXT);
CREATE OR REPLACE FUNCTION my_func()
RETURNS my_type AS
$BODY$
DECLARE
m my_type;
BEGIN
SELECT my_table::TEXT
INTO m.result
FROM my_table;
RETURN m;
END
$BODY$
LANGUAGE plpgsql STABLE;
Run:
SELECT (m.result::my_table).*
FROM my_func() AS m
Result:
| id | txt |
-------------
| 1 | blah |

Drools rules with boolean variable

I want to declare a boolean variable in my drl file and wants to write a rule based on the value of that variable. I am not able to find any good example for this.
I tried like:
declare Flag
flag: Boolean
end
In one of the rule, I am modifying like:
flag = Boolean.TRUE;
and my rule is:
rule "<210> Determine flag"
when
Flag(flag == true)
...
end
But it is giving me error as flag cannot be resolved.
You cannot declare variables in the usual way as (I think) you are trying to do. Please read the Drools documentation and distinguish binding variables, global variables, facts and their fields and right hand side local variables (as within a static Java method).
To see how declare with a boolean field works, use the following DRL code:
declare Flag
flag: Boolean
end
rule "hoist a Flag"
when
not Flag()
then
insert( new Flag( true ) );
end
rule "a true Flag"
when
Flag( flag )
then
System.out.println( "The Flag.flag is true." );
end
You can use the following:
rule "Test"
when
a: TestClass( getFlag())
then
//some action
end
Also see this answer: Drools Rule - writing rule against boolean field, name starting with "is"

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.

PL/pgSQL , How to make a function using Raise notices and export the messages from the console to a text file from the Code

I have to make a update function that have multiple conditions like this
BEGIN
OPEN cur3 FOR execute('select id_organigramme from ( select distinct id_personne,id_organigramme,idfax from requpdate where
id_personne= ' || VariableIDpersonne || ' and idfax is null) a where
a.id_organigramme not in (select distinct id_organigramme from
requpdate where id_personne= ' ||VariableIDpersonne || ' and idfax is
not null and a.id_personne=requpdate.id_personne ) ');
LOOP
FETCH cur3 INTO VariableIDorganigrammeFax;
if not found then
--Message here !!!
--Raise notice 'hello word!'
exit;
end if;
I have to show up messages if any condition exists I found out that I can do this with Raise Notice/info ... statement, but I have to make auto export of those messages into a text file when the function finishes.
Is this possible? Otherwise what can I use to make it.
I use PGAdminIII as a client.
What your logging options are depends entirely on your client configuration. But rather than using RAISE NOTICE I would suggest you use the NOTIFY \ LISTEN framework. Basically, in your function you issue a notice to a channel of your choosing (can be any string) and in your client you listen to that same channel, logging the messages as they come in. How exactly the listening and logging works depends on your client.
The code you show can also you use some improvements.
First of all, your query is an incredibly convoluted version of:
SELECT DISTINCT id_organigramme
FROM requpdate
WHERE id_personne = VariableIDpersonne
AND idfax IS NULL;
Secondly, you do not need a dynamic query, you can get by with variable substitution. Assuming id_personne is not a string, it is as simple as stated above, otherwise use quote_literal(VariableIDpersonne).
Lastly, unless there are parts of your function not shown that require a cursor, you can simply do:
FOR VariableIDorganigrammeFax IN [query above]
LOOP
... -- do your processing here
END LOOP;
IF NOT FOUND THEN -- the loop above did not iterate because no records were returned
SELECT pg_notify('logMyFunction', format('%s: No records found', VariableIDpersonne));
END IF;
The pg_notify() function is a wrapper around the NOTIFY command that makes it possible to pass variable strings.
Before you call the function, you should issue the command LISTEN logMyFunction so that your session will receive the notifications from the channel.

How can i store the result of a comparison into a variable

I want to print a simple statement
print (1=1), i expect the result to be TRUE or 1 but sql server tell me:
Incorrect syntax near '='.
why is that?
Same will happen for a statement like that
declare #test bit
set #test = (1=1)
in summary how can i "see" what is returned from a comparison without using an IF statement
Update: The reason i'm asking is because i'm trying to debug why the following statement
declare #AgingAmount smallint
set #AgingAmount = 500
select Amount, datediff(day,Batch.SubmitDate,getdate()) as Aging from myreporrt
where datediff(day,Batch.SubmitDate,getdate()) > #AgingAmount
will return all rows even with aging of 300
so i wanted to test if datediff(day,datesubmited,getdate()) > 500 returns true or false but could not find a way how to display the result of this comparison.
Although SQL Server has the concept of aboolean type, and it understands expressions that resolve to a boolean in IF and WHERE clauses, it does not support declaring boolean variables or parameters. The bit data type cannot store the result of a boolean expression directly, even though it looks suspiciously like one.
The nearest you can get to a boolean data type is this:
-- Store the result of a boolean test.
declare #result bit
select #result = case when <boolean expression> then 1 else 0 end
-- Make use of the above result somewhere else.
if #result = 1
...
else
...
To add to the confusion, SQL Server Management Studio treats bit like boolean when displaying results, and ADO.NET maps bit to System.Boolean when passing data back and forth.
Update: To answer your latest question, use the case when ... then 1 else 0 end syntax in the select statement.