× The internal search function is temporarily non-functional. The current search engine is no longer viable and we are researching alternatives.
As a stop gap measure, we are using Google's custom search engine service.
If you know of an easy to use, open source, search engine ... please contact support@midrange.com.



On 17 Apr 2013 11:28, Vern Hamberg wrote:

I'll have to think about the OVRDBF - I've run into issues with
redirecting output from QShell to a PF, but maybe that's not an
issue here - I mean, the PF has to exist already. Well, I've
answered myself, I think, but will look at this for future
reference, not just in an SQL procedure.

My comment was at first directed toward the fact that calling QCMDEXC from RPG to effect QSH CMD() processing seemed to involve a fair amount of indirection; perhaps to ease redirection.? Without actually asking, I was wondering if there might not be an API to effect asking directly of the LDAP or calling the QSHELL directly, to offer a bit more direct access to the data? With the former, I would expect that an external UDTF would be able to be created to expose the LDAP data to and via the SQL; such a UDTF could be used on the INSERT.

http://pic.dhe.ibm.com/infocenter/iseries/v7r1m0/topic/apis/dirserv2.htm
_i LDAP API Overview i_
"...
Typical API usage

The basic interaction is as follows. A connection is made to an LDAP server by calling ldap_init (or ldap_ssl_init, which is used to establish a secure connection over Secure Sockets Layer (SSL)).

An LDAP bind operation is performed by calling ldap_simple_bind or ldap_sasl_bind. The bind operation is used to authenticate to the directory server. Note that the LDAP V3 API and protocol permits the bind to be skipped, in which case the access rights associated with anonymous access are obtained.

Next, other operations are performed by calling one of the synchronous or asynchronous routines (that is, ldap_search_s or ldap_search followed by ldap_result).

Results returned from these routines are interpreted by calling the LDAP parsing routines, which include operations such as:

* ldap_first_entry, ldap_next_entry
* ldap_get_dn
* ldap_first_attribute, ldap_next_attribute
* ldap_get_values
* ldap_parse_result (new for LDAP V3)
* etc.

..."

But more importantly, I was wondering because the work is spawned. I was confused about how redirection into a file in QTEMP would function without an OVRDBF to STDOUT. For example, the following script using an override to the Standard Output works fine, but redirecting to a database file in QTEMP [¿or even to any described non-source physical file?] would fail within the script.?:

declare global temporary table session/ldapattrs
( attr for ldapattrs char(100) not null with default '' )
ovrdbf stdout tofile(qtemp/ldapattrs)
qsh cmd('ldapsearch -e') /* a simple functional request */
--Command ended normally with exit status 0.
runqry *n ldapattrs
Line ....+....1....+....2....+....3....+ <...> 9....+...10
ATTR
000001
000002 SDK Version: 510
000003 Protocol Version: 300
000004 SDK Build Level: V5R3M0
****** ******** End of report ********

The LDAP can look like this - varying number of attributes per
account:

CN=Smith\
sn=Smith
givenName=John
displayName=Smith
employeeNumber=12345
sAMAccountName=SMITHJ

CN=Anderson\
sn=Anderson
givenName=Anders
initials=D
displayName=Anderson
employeeNumber=98765
sAMAccountName=ANDERAD
mail=aanderson@xxxxxxxxxxx

The earlier comment suggesting "I use the CN= as a level-break of sorts" seems to imply a good possibly that there is a dependency on the use of the /arrival sequence/ for that data, so the following comments will include an ORDER BY RRN() to ensure that assumption is met. FWiW it seems from the LDAP command-line interface, with regard to the *Output Format* that "Multiple entries are separated with a single blank line." So I suppose the blank line could be more important than a "CN=" if perhaps a new group might start with something else.?

I've not studied SQL arrays enough to know if they'd help.

Perhaps not, but they offer what seems a nice interface to load the data without the [CLOSE,]OPEN,FETCH,CLOSE. But that is also available without using any ARRAY support; see first example loading an ARRAY.

First, create an ARRAY type that the procedure would be able to use.

CREATE TYPE vpArray AS VARCHAR(100) ARRAY[300]
;

In the procedure, something like one of the following probably suffices to load the array. The first avoids the OPEN\CLOSE of the cursor, and replaces FETCH with SET statements. Any of these could be extended to divide the value pairs into both attributeType and attributeValue with just another array declaration and a SET from a function invocation that obtains each portion from the field. I have never actually used the SQL ARRAY support, so I may have some syntax wrong or be unaware of limitations:


declare vpElem smallint default 0 ;
declare vpList vpArray ;
For vp as ValuePairs cursor for
select strip(ldapattrs) /* or RTRIM */
from SESSION/LDAPATTRS as Attr
where ldapattrs<>''
order by rrn(Attr)
Do
set vpElem = VpElem + 1 ;
set vpList[vpElem] = vp.ldapattrs ;
End For;


declare vpList vpArray ;
set vpList =
(select ARRAY_AGG(strip(ldapattrs) ORDER BY rrn(Attr))
from SESSION/LDAPATTRS as Attr
where ldapattrs<>''
) ;


declare vpList vpArray ;
select ARRAY_AGG(strip(ldapattrs) ORDER BY rrn(Attr))
into vpList
from SESSION/LDAPATTRS as Attr
where ldapattrs<>''
;

This isn't supposed to get too complicated, so I'm happy enough
with what we got. So's my boss, which is the best thing!

FWiW: Seems an odd request to get that data into a database TABLE to be processed. I would think whatever needs to be done likely has been done before, in scripting languages and tooling.

As to complicated:
- Why use dynamic SQL for the SELECT?
- There are plenty of other ways than to prime the looping with a FETCH [outside the loop], to process fetched row data within a loop. See for example, the FOR loop in the example above; i.e. that example need not be limited solely for populating an ARRAY.

Interesting idea about filtering against blank lines, but I'd
still need the ELSE, even though it'd never be executed, right?

The ELSE [with a begin\end, a LEAVE, a SET var to self, a GOTO, a hard SIGNAL, an /assertion/ with SIGNAL only if failed, or ??] or the monitor, I suppose. Note: the ITERATE could function as well in that context, if the only FETCH were inside and just after the WHILE; followed by an IF AT_END=1 THEN LEAVE; END IF;

Without more details, it is difficult to know if there might be better ways to accomplish what needs to get done. The deliberately /omitted/ details leave only a minimal explanation of how the CASE is really being utilized. I infer the effect is to set a specific variable name to the attributeValue based upon the attributeType for the row; the INSERT it seems must be one row with a column for each attributeValue per the description of the WHENs in the CASE, and I suppose that is consistent with, that for the value pairs, the aim is to "flatten them into rows". FWiW: A more complete description of what is the DDL of the TABLEs involved and what needs to be done might allow someone to offer alternate solutions; e.g. one that could eliminate the CASE statement.

Regards, Chuck

On 17 Apr 2013 10:45, CRPence wrote:

You might want to look here for scoping the condition handler(s):
http://pic.dhe.ibm.com/infocenter/iseries/v7r1m0/topic/sqlp/rbafyredesign.htm

To skip a blank row, why not include a WHERE clause in the SELECT?
A simple case of overlooking the obvious? That is:
'select ldapattrs from SESSION/LDAPATTRS where ldapattrs<>'''''

The CL interface to invoke QShell is to effect an OVRDBF STDOUT
TOFILE(QTEMP/LDAPATTRS) in order to redirect the standard output
within that job, from the QSH CMD('ldap_request'), into the GTT?

I wonder if you could use SQL ARRAY support instead of using a
CURSOR with FETCH; load the results of the SELECT directly into an
ARRAY.

An example of some actual effects of the LDAP data in the file
would be more appropriate for reviewing... as I [nor likely others
will] have no ability to effect such a request... and even what
request was made of the LDAP was unstated.

On 15 Apr 2013 11:19, Vern Hamberg wrote:
<<SNIP>>

The other procedure calls QCMDEXC to run a QShell function that
returns Active Directory attributes as name-value pairs. I need
to flatten them into rows. I have asked for exactly 7 attributes,
not all are returned for each AD name, and there is a CN= pair,
as well as a blank row between each group.

The only one that doesn't matter to me is the blank row - I use
the CN= as a level-break of sorts. I do have an else here, which
really handles the blank row. I suppose ITERATE could take care
of me here. But I think not - I have a FETCH right after the
CASE, within a WHILE loop, and that would be skipped with an
ITERATE.

Putting the CASE in a compound statement - that would work
nicely, too. It fits my structure nicely, I think.

Maybe a hint at the flow will help - some declarations and other
details are omitted

/* Variables */
DECLARE AT_END INT DEFAULT 0;

/* Cursors */
/* Get Active Directory attributes */
DECLARE C1 CURSOR FOR S1;

/* Handlers */
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET AT_END = 1;

/* Global temporary table for LDAP attributes */
DECLARE GLOBAL TEMPORARY TABLE SESSION/LDAPATTRS
(ldapattrs CHAR(100));

/* Pull attributes into temp table */
set sqlStmt = 'select * from qtemp/ldapattrs';
prepare s1 from sqlStmt;
OPEN C1;
FETCH C1 INTO ldaptext;

/* Walk through specialist IDs */
WHILE AT_END = 0 DO

case
-- several WHENs that each set a variable from a name-value pair
-- the variable is used later in an INSERT when a CN= is encountered
else
set at_end = at_end; /* Dummy operation */
end case;

/* Get next ID */
FETCH C1 INTO ldaptext;

END WHILE;



As an Amazon Associate we earn from qualifying purchases.

This thread ...


Follow On AppleNews
Return to Archive home page | Return to MIDRANGE.COM home page

This mailing list archive is Copyright 1997-2024 by midrange.com and David Gibbs as a compilation work. Use of the archive is restricted to research of a business or technical nature. Any other uses are prohibited. Full details are available on our policy page. If you have questions about this, please contact [javascript protected email address].

Operating expenses for this site are earned using the Amazon Associate program and Google Adsense.