How to create a temp table using dynamic query - progress-4gl

CREATE QUERY ohQuery.
ohQuery:SET-BUFFERS(hBuffer).
OhQuery:QUERY-PREPARE("FOR EACH " + ip-tablename ).
ohQuery:QUERY-OPEN().
ohQuery:GET-FIRST().
ip-tablename = value from UI (combo box)
Now I need to create a temp-table for this query. How to create?
If I am using create-like method what value should I pass in this?

To create a temp-table dynamically at runtime, you need either a handle to a buffer of said table, or the table-name itself. Seeing as you already have the handle to the buffer of 'ip-tablename' (I am assuming hBuffer is a buffer to the table with name 'ip-tablename', otherwise your above statement won't work), you should use that handle instead of 'just' the table-name out of performance reasons. Here's how to do it:
DEF VAR ttH AS HANDLE NO-UNDO.
ttH:CREATE-LIKE(hBuffer).
Optionally, you can also use 'ip-tablename' for the create-like method, although it doesn't perform as well:
ttH:CREATE-LIKE(ip-tablename).
Don't forget that you'll need to use the temp-table prepare method before you can use your new temp-table:
ttH:TEMP-TABLE-PREPARE("myNewTempTable").
Hope that helps!

Related

Is it possible to remove column from the GetTableSql() in BIML

I use the GetTableSql() function in BIML a lot, but I often need to remove some columns from this function before it executes. Is this possible?
You'd have to write your own Extension method to do so. Looking through the code, you might be better off just scrubbing the column(s) from results of the method call - it just depends on what you're looking to do.
The current GetTableSql method is an extension method that chains a call to EmitTableScript which in turn calls a number of methods to build out the returning SQL. At least in the BimlStudio product, EmitTableScript is in Varigence.Biml.CoreLowerer.Capabilities.TableToPackageLowerer class in BimlExtensions.dll
After a bit more thinking, what might be an even better, less support headache would be to create a clone of the table node and then remove the columns you don't want.
Code approximately
var table0 = this.RootNode.Tables[0];
var tablePrime = table0;
// I don't have a biml project handy so this section is a guess
tablePrime.Columns.Clear();
// Might be AddRange if this method exists
// Remove all the columns that start with ignore, as an example of filtering columns
tablePrime.Columns.Add(table0.Columns.Where(x => !x.name.StartsWith("ignore"));
// end guess block
var sql = tablePrime.GetTableSql();

SCL code: tag FUNCTION not defined on TIA

I want to create a new SCL function with TIA 15.1. I chose Program blocks => Add new block => Function. When I write the keyword FUNCTION in my file I get the following error: tag FUNCTION not defined. This is the same for others keywords like VAR for example.
My Code:
FUNCTION "test" :Void
VAR
V1: Bool;
END_VAR
END_FUNCTION
I searched the web but there is not a lot of documentation on the topic.
Any idea to solve the problem?
By default the way you need to do this is a little different than the old stuff. You need to define your variables in the table and and just write code in the editor. You don't add the function, var, var_in, etc. keywords.
However, new to V15.1 you can select text view under Options->Settings->PLC Programming->SCL->Interface and select Text view. If you create a new function now it will let you enter the variables using var, var_in, etc. But make sure you create a new function block. Also, this only works with SCL.

Zend\db\sql - prepareStatementForSqlObject - still need to bind or worry about sql injection?

I'm using zf 2.4 and for this example in Zend\db\sql. Do I need to worry about sql injection or do I still need to do quote() or escape anything if I already use prepareStatementForSqlObject()? The below example will do the blind variable already?
https://framework.zend.com/manual/2.4/en/modules/zend.db.sql.html
use Zend\Db\Sql\Sql;
$sql = new Sql($adapter);
$select = $sql->select();
$select->from('foo');
$select->where(array('id' => $id));
$statement = $sql->prepareStatementForSqlObject($select);
$results = $statement->execute();
The Select class will cleverly check your predicate(s) and add them in a safe manner to the query to prevent SQL-injection. I'd recommend you to take a look at the source for yourself so I'll point you to the process and the classes that are responsible for this in the latest ZF version.
Predicate Processing
Take a look at the class PredicateSet. The method \Zend\Db\Sql\Predicate::addPredicates determines the best way to handle your predicate based on their type. In your case you are using an associative array. Every item in that array will be checked and processed based on type:
If an abstraction replacement character (questionmark) is found, it will be turned into an Expression.
If the value is NULL, an IS NULL check will be performed on the column found in the key: WHERE key IS NULL.
If the value is an array, and IN check will be performed on the kolumn found in the key: WHERE key IN (arrayVal1, arrayVal2, ...).
Otherwise, the predicate will be a new Operator of the type 'equals': WHERE key = value.
In each case the final predicate to be added to the Select will be implementing PredicateInterface
Preparing the statement
The method \Zend\Db\Sql\Sql::prepareStatementForSqlObject instructs its adapter (i.e. PDO) to create a statement that will be prepared. From here it gets a little bit more complicated.
\Zend\Db\Sql is where the real magic happens where in method \Zend\Db\Sql::createSqlFromSpecificationAndParameters the function vsprintf is used to build the query strings, as you can see here.
NotePlease consider using the new docs.framework.zend.com website from now on. This website is leading when it comes to documentation of the latest version.

Edit `TABLE-HANDLER` to point to another temp-table

I'm trying to overwrite the parameter handler, so it'd point to another temp-table with different schema.
myProcedure.p
DEFINE TEMP-TABLE TT_RealTable NO-UNDO
BEFORE-TABLE TT_RealTableBefore
FIELD name AS CHAR
.
DEF INPUT-OUTPUT PARAM TABLE-HANDLE TH_NewTable.
CREATE TT_RealTable.
ASSIGN TT_RealTable.name = "Ben".
CREATE TT_RealTable.
ASSIGN TT_RealTable.name = "Harry".
The TH_NewTable points to a random parameter TEMP-TABLE with different schema and contents. I want to overwrite this parameter TABLE-HANDLER with the TT_RealTable. If I try to set:
TH_NewTable = TEMP-TABLE TT_RealTable:HANDLE.
The program will crash at runtime saying that TT_RealTable's schema/meta data doesn't match the handler's one.
How I can overwrite the handler to point to my TT_RealTable:HANDLE, and return it as output?
If the schema IS different as the error claims, and you said at the beginning of your post, what you want to do can't be done. You can't pass a temp-table buffer handle that has a certain definition and return something else at the end of the procedure, assuming the parameter table handle is from a static temp-table. Even if it isn't, you'd need to create the temp-table using the buffer handle AFTER calling the procedure, which I'm thinking is not what you want, since your parameter is input-output.
If you really want to work with a table that's fluid as far as schema goes, you should read into dynamic temp-tables. Look for the CREATE TEMP-TABLE statement in help and read through the examples to understand how you can get a handle and then (and only then) build a temp-table using the returned handle, so you can use that. You lose the ability of using regular statements, though, such as create and assign. You'll have to reference BUFFER-FIELD and BUFFER-VALUE, and use the CREATE method instead, using the buffer-handle. Might take a while to get used to it, but it's not rocket science.
Hope it helps!

how to deal with complex branching in iReport or Jasper

I need to fill a field in a report based on the end result of a complex branching statement. How do I do this in iReport? The report needs to show a different string depending on what is in different fields in the database. Do I make a really complex SQL statement? Do I use variables?
So, for instance,
If field x=1
IF y=1
IF z=1
Field should read A
If x=1
IF y=1
IF z=2
Field should read B
You could do something similar to the following:
( $F{staff_type} == null ? new String("") :
( $F{staff_type}.equalsIgnoreCase("Permanent") ? new String("1") :
( $F{staff_type}.equalsIgnoreCase("Non-permanent") ? new String("2") : new String("")
)))
Basically, you need to use nested condition expressions.
in the textfieldexpression write an expression like this
(($F{PAYMENTMODE}.equals("CS")) ? "Cash":($F{PAYMENTMODE}.equals("CQ"))? "Cheque":"Bank")e
I think the easiest way to do this would be to have the field(s) filled by a parameter passed from the backing bean. The jdbc connection is created in the bean and passed to the report, it should be relatively easy to access the field or fields you need and run the data through a method which determines the branching outcome. Assign the outcome to the parameter and pass it to the report in the jasperParameter variable of JasperFillManager.fillReport(file, parameters, jdbcConnection).
Usually, I handle all the programming logic before passing the data to the Jasper Report Engine, but in some cases, post-processing or post-checking is required. If it is that scenario and If I had MANY cases (strings) to check, I would code a 'Jasper Report Scriptlet' and handle that logic there (so that the code/report is readable and maintainable and also for code reuse). If it is just 2 or 3 strings to check, I would use the 'Ternary' operator.
If you want to use report scriptlet, create a scriptlet class (or use an existing one), code a method to handle this logic (Ex: 'checkString' method) and put $P{REPORT_SCRIPTLET}.checkString(someString) in the TextField's expression.