Parsing XML with DFSORT

(Originally posted 2006-04-25.)

Following on from Generating XML Using DFSORT – Part II here are some thoughts on how to parse XML with DFSORT.

NOTE: For more complex XML than this entry describes you probably want to use the XML Toolkit for z/OS. This provides C++ and Java parsers for XML and a stand-alone XSLT processor.

In this example I'll show you how to take XML that looks like this and create a flat file from it:

<?xml version="1.0" encoding="UTF-8" ?>
<band>
<member surname="Mercury" firstname="Freddie" job="Singer" />
<member surname="May" firstname="Brian" job="Guitarist" />
<member surname="Taylor" firstname="Roger" job="Drummer" />
<member surname="Deacon" firstname="John" job="Bassist" />
</band>

and turn it into our (now familiar)

Mercury         Freddie         Singer   
May             Brian           Guitarist
Taylor          Roger           Drummer  
Deacon          John            Bassist  

which could be mapped with DFSORT Symbols:

Surname,*,16,CH  
Firstname,*,16,CH
Job,*,10,CH

In fact – in this example – we won't use these symbols.

The first thing we need to do is to keep only the data rows – and to do that we code:

  INCLUDE COND=(1,7,CH,EQ,C'<member')

which throws away the first two rows and the last row.

Next we need to parse the data rows using the following INREC statement:

INREC IFTHEN=(WHEN=INIT,
        PARSE=(%1=(STARTAFT=C'<',ENDBEFR=C'/>',FIXLEN=80)),
        BUILD=(%1)),
      IFTHEN=(WHEN=INIT,
        PARSE=(%2=(ABSPOS=1,FIXLEN=18,STARTAFT=C'surname="',ENDBEFR=C'"')),
        BUILD=(%2,1,80)),
      IFTHEN=(WHEN=INIT,
        PARSE=(%3=(ABSPOS=1,FIXLEN=18,STARTAFT=C'firstname="',ENDBEFR=C'"')),
        BUILD=(1,16,%3,1,80)),
      IFTHEN=(WHEN=INIT,
        PARSE=(%4=(ABSPOS=1,FIXLEN=10,STARTAFT=C'job="',ENDBEFR=C'"')),
        BUILD=(1,32,%4))

which looks rather complicated.

This uses IFTHEN (introduced in 2004) and PARSE (introduced in 2006 with UK90006/UK90007).

In fact the IFTHEN clauses are a pipeline of stages. The WHEN=INIT conditions mean that they are performed for all records that pass the INCLUDE statement's condition. Each WHEN=INIT is performed in turn. And all the stages are performed within the INREC statement. That is, before any SORT, SUM, OUTREC or OUTFIL processing.

The first stage in this pipeline is

      IFTHEN=(WHEN=INIT,
        PARSE=(%1=(STARTAFT=C'<',ENDBEFR=C'/>',FIXLEN=80)),
        BUILD=(%1)),

which strips off the surrounding angle brackets from each line, producing an 80-byte record.

The second stage is

   IFTHEN=(WHEN=INIT,
        PARSE=(%2=(ABSPOS=1,FIXLEN=18,STARTAFT=C'surname="',ENDBEFR=C'"')),
        BUILD=(%2,1,80)),

which extracts the surname attribute into a field which is prepended onto the 80-byte record created in the first stage.

The third stage is

   IFTHEN=(WHEN=INIT,
        PARSE=(%3=(ABSPOS=1,FIXLEN=18,STARTAFT=C'firstname="',ENDBEFR=C'"')),
        BUILD=(1,16,%3,1,80)),

which extracts the firstname attribute into a field which is prepended onto the 80-byte record created in the first stage (but after the 16-byte (surname) field extracted in the second stage).

The fourth and final stage is

   IFTHEN=(WHEN=INIT,
        PARSE=(%4=(ABSPOS=1,FIXLEN=10,STARTAFT=C'job="',ENDBEFR=C'"')),
        BUILD=(1,32,%4))

which extracts the job attribute into a field which is appended onto the the 16-byte (surname) field extracted in the second stage and the 16-byte (firstname) field extracted in the third stage.

I admit this looks complicated but it does allow for the attributes to appear in any order in an XML element. What it doesn't do is to allow any old multiple-line format for the input XML. For that you really do need the toolkit. But I'm convinced there are tricks we can teach DFSORT when it comes to parsing XML. It's just that we'd need time to think about them. 🙂

A note on pipelining: When I first saw IFTHEN I thought of it potentially as a pipelining technique. This example is quite a good one for pipelining as everything happens in 4 IFTHEN WHEN=INIT stages. It's actually proved a lot simpler to construct the DFSORT processing this way – and it has isolated all the processing to the INREC statement. So there's lots you can do later on in the DFSORT invocation. (And the ability to allow the attributes (surname, firstname and job) to be in any order was made much easier by this pipelining approach.

But I have to be sanguine about pipelining: At this stage in DFSORT's development we don't have all the capabilities for branching etc that CMS (/TSO) Pipelines has. But I offer you the pipelining model as another way of thinking about what IFTHEN can do, as well as the "treat different records in different ways" original intention. However, we do have nice constructs like WHEN=ANY, WHEN=NONE and HIT=NEXT to construct reasonable pipelines with.

What has been really nice about recent DFSORT innovations is that you find out more things you can do with them every day.

PARSE is worthy of some more discussion: It's brand new (April 2006) and allows DFSORT to parse (duh!) variable-format data. In this case the length of each attribute is variable. %1, %2, %3 and %4 refer to different variable-length fields that we can use in subsequent processing stages. Let's take one usage as an example:

   IFTHEN=(WHEN=INIT,
        PARSE=(%1=(STARTAFT=C'<',ENDBEFR=C'/>',FIXLEN=80)),
        BUILD=(%1)),

In this case we extract into the variable %1 the first string in the input record that starts with < and ends with />, padding to 80 bytes with blanks. The BUILD=(%1) says that the output from this IFTHEN (stage) will be that entire 80-byte %1 variable. That is the input record but with the top and tail removed.

Note: We don't actually need to know how long the string we're extracting is. Prior to PARSE we would've had to know. And that, to me, is one of the very nice features of PARSE.

Generating XML Using DFSORT – Part II

(Originally posted 2006-04-24.)

In Generating XML Using DFSORT – Part I I described how to generate XML using some of DFSORT’s new features.

But the XML we generated was only one of the possible styles of XML: Most of the information was specified as attributes (with the values enclosed in quotes). Here’s a sample line:

<member surname="Mercury" firstname="Freddie" job="Singer" />

In this case the attribute job has the value Singer.

Another style uses elements rather than attributes. Here’s an example that conveys the same information (but in XML terms is not equivalent):

  <member>
    <surname>Mercury</surname>
    <firstname>Freddie</firstname>
    <job>Singer</job>
  </member>

The following example is similar to the one in User Guide for DFSORT PTFs UK90007 and UK90006 (SORTPEUG), but it builds on the example in Part I.

Starting with the same set of data

Mercury         Freddie         Singer   
May             Brian           Guitarist
Taylor          Roger           Drummer  
Deacon          John            Bassist  

and the same Symbols mapping it

Surname,*,16,CH  
Firstname,*,16,CH
Job,*,10,CH

We’d like to create an XML file:

<?xml version="1.0" encoding="UTF-8" ?>
<band>
  <member>
    <surname>Mercury</surname>
    <firstname>Freddie</firstname>
    <job>Singer</job>
  </member>
  <member>
    <surname>May</surname>
    <firstname>Brian</firstname>
    <job>Guitarist</job>
  </member>
  <member>
    <surname>Taylor</surname>
    <firstname>Roger</firstname>
    <job>Drummer</job>
  </member>
  <member>
    <surname>Deacon</surname>
    <firstname>John</firstname>
    <job>Bassist</job>
  </member>
</band>

The first step is to generate the data records (the ones bracketed by <member;> and </member>). Here is the OUTFIL statement that does this:

OUTFIL FNAMES=OUT1,REMOVECC,
  BUILD=(3:C'<member>',/,
         5:Surname,JFY=(LENGTH=35,
           SHIFT=LEFT,LEAD=C'<surname>',TRAIL=C'</surname>'),/,
         5:Firstname,JFY=(LENGTH=39,
           SHIFT=LEFT,LEAD=C'<firstname>',TRAIL=C'</firstname>'),/,
         5:Job,JFY=(LENGTH=27,
           SHIFT=LEFT,LEAD=C'<job>',TRAIL=C'</job>'),/,
         3:C'</member>')

In this case I didn’t actually use the Symbol for Position capability just introduced by UK90006 / UK90007, but I could have. But I did use the JFY function, also new with that level of DFSORT. Its purpose, just like in Part I, is to squeeze out trailing blanks from the fixed-width fields.

NOTE: The use of REMOVECC is important to avoid generating ANSI control characters, which would not be valid XML.

At this stage the output would be

  <member>
    <surname>Mercury</surname>
    <firstname>Freddie</firstname>
    <job>Singer</job>
  </member>
  <member>
    <surname>May</surname>
    <firstname>Brian</firstname>
    <job>Guitarist</job>
  </member>
  <member>
    <surname>Taylor</surname>
    <firstname>Roger</firstname>
    <job>Drummer</job>
  </member>
  <member>
    <surname>Deacon</surname>
    <firstname>John</firstname>
    <job>Bassist</job>
  </member>

To add the first 2 lines and the final line to this we code additional OUTFIL parameters:

  HEADER1=('<?xml version="1.0" encoding="UTF-8" ?>',/,'<band>'),
  TRAILER1=('</band>')

making the entire OUTFIL statement:

OUTFIL FNAMES=OUT1,REMOVECC,
  BUILD=(3:C'<member>',/,
         5:Surname,JFY=(LENGTH=35,
           SHIFT=LEFT,LEAD=C'<surname>',TRAIL=C'</surname>'),/,
         5:Firstname,JFY=(LENGTH=39,
           SHIFT=LEFT,LEAD=C'<firstname>',TRAIL=C'</firstname>'),/,
         5:Job,JFY=(LENGTH=27,
           SHIFT=LEFT,LEAD=C'<job>',TRAIL=C'</job>'),/,
         3:C'</member>'),
  HEADER1=('<?xml version="1.0" encoding="UTF-8" ?>',/,'<band>'),
  TRAILER1=('</band>')

You can use this statement with statements such as SORT, COPY, INCLUDE, OMIT, INREC and OUTREC, so long as the Symbols set you use reflect any reformatting of the records passed into OUTFIL.

In this entry and Part I I’ve shown you a couple of techniques for generating XML, which can be adapted in a mix and match fashion. In the next entry in this series I’ll start to describe how to parse XML.

Generating XML Using DFSORT – Part I

(Originally posted 2006-04-24.)

Following on from This entry on creating CSV files here’s the first of several entries on manipulating XML with DFSORT…

This is not intended to be a tutorial on XML but rather an exploration of how DFSORT can read (shred) and write (compose) XML. (I also use the terms ingest and emit synonymously.) Hopefully some of the basic concepts of XML will come across during the course of these blog entries.

XML is – to my mind – much better than CSV…

  • It IS a standard – and lots of things are built on this standard.
  • There is semantic and structuring information built into XML.
  • Support for reading and writing XML are built into many programming languages and other programs, such as DB2.

So XML is a great way of structuring information for exchange between programs and systems.

So we’ll start with a simple set of data we’d like to convert to XML…

Consider the small data set

Mercury         Freddie         Singer   
May             Brian           Guitarist
Taylor          Roger           Drummer  
Deacon          John            Bassist

This data is mapped with the following DFSORT Symbols deck:

Surname,*,16,CH
Firstname,*,16,CH
Job,*,10,CH

We’d like to create an XML file:

<?xml version="1.0" encoding="UTF-8" ?>
<band>
	<member surname="Mercury" firstname="Freddie" job="Singer" />
	<member surname="May" firstname="Brian" job="Guitarist" />
	<member surname="Taylor" firstname="Roger" job="Drummer" />
	<member surname="Deacon" firstname="John" job="Bassist" />
</band>>

The first thing we do is to take the input records and wrap them with quotes, squeezing out the trailing spaces:

<member surname="Mercury"          firstname="Freddie"          job="Singer"    />
<member surname="May"              firstname="Brian"            job="Guitarist" />
<member surname="Taylor"           firstname="Roger"            job="Drummer"   />
<member surname="Deacon"           firstname="John"             job="Bassist"   />

To do this we can use the following INREC statement:

INREC BUILD=(C'<member',
    C'surname=',Surname,JFY=(SHIFT=LEFT,LEAD=C'"',TRAIL=C'"',LENGTH=18),X,
    C'firstname=',Firstname,JFY=(SHIFT=LEFT,LEAD=C'"',TRAIL=C'"',LENGTH=18),X,
    C'job=',Job,JFY=(SHIFT=LEFT,LEAD=C'"',TRAIL=C'"',LENGTH=12),
    C'/>')

The above statement reformats the three fields – removing the trailing spaces – in the manner described in my CSV creation blog entry.

The next (OUTREC) statement squeezes out the spaces, preserving those within the quotes:

   OUTREC BUILD=(_Unsqueezed,SQZ=(SHIFT=LEFT,PAIR=QUOTE,MID=C' '))

To make the above code work I defined an additional symbol

_Unsqueezed,1,82,CH

This symbol maps the whole of the output of the INREC statement – that is the records wrapped in quotes.

The final step is an OUTFIL statement that tops and tails the output data with some more tags:

OUTFIL FNAMES=OUT1,REMOVECC,
    HEADER1=('<?xml version="1.0" encoding="UTF-8" ?>',/,'<band>'),
    TRAILER1=('</band>')

NOTE: REMOVECC removes the ANSI carriage control bytes.

In this example I’ve spread the transformations across INREC, OUTREC and OUTFIL statements. You might want to place the function differently: You might, for instance, want to do a SORT or include only certain records upstream of the XML creation. Perhaps in a later blog entry I’ll talk about that some more. But there are other things I want to talk about regarding DFSORT and XML. Stay tuned for a different style of XML.

Using DFSORT to create CSV files

(Originally posted 2006-04-21.)

I plan on writing some entries on creating and parsing XML with DFSORT (using the UK90006 / UK90007 functional enhancments that DFSORT Development recently announced). But here’s a limbering up example – creating a CSV file from regular sequential file input.

CSV files (Comma-Separated Value (or Variable if you prefer)) are of the form

"JDLFJDJ DF",4146,"FKJFK"
"JDJDJ JKJJ",12352,"EE FF"
"AAFIELD3FI",4,"94949"
"ACFIELD",35,"34443"

where the commas separate fields, and where the quotes denote their contents are character strings. Each line is a separate row of fields. So it’s really a grid. This is an early form at structuring data as text, and it’s used by many programs such as spreadsheets. It’s not a terribly robust format and probably isn’t a standard. Further, there is no real attempt to define the meaning of the fields.

But it does illustrate a use for the new JFY and SQZ capabilities of DFSORT…

The source data for this example is

JDLFJDJ DF    FKJFK
JDJDJ JKJJ    EE FF
AAFIELD3FI    94949
ACFIELD       34443

where the blanks in the middle are actually a 4-byte binary number.

The DFSORT control statements are…

OPTION COPY
INREC BUILD=(STR1,JFY=(SHIFT=LEFT,LEAD=C'"',TRAIL=C'"',LENGTH=12),X,
             NUM1,EDIT=(IIIIIIIT),X,
             STR2,JFY=(SHIFT=LEFT,LEAD=C'"',TRAIL=C'"',LENGTH=7))
OUTREC BUILD=(PRINTED,SQZ=(SHIFT=LEFT,PAIR=QUOTE,MID=C','))

You’ll notice the widespread use of Symbols, which isn’t a new thing. So here’s the Symbols deck:

//SYMNAMES DD *
POSITION,1           
STR1,*,10,CH         
NUM1,*,4,BI          
STR2,*,8,CH          
*
PRINTED,1,29,CH      

The first four symbols map the input record. The fifth one (PRINTED) maps the intermediate record that results from the INREC.

The INREC statement produces (with the sample data) the following intermediate records:

"JDLFJDJ DF"     4146 "FKJFK"
"JDJDJ JKJJ"    12352 "EE FF"
"AAFIELD3FI"        4 "94949"
"ACFIELD"          35 "34443"

So the strings are wrapped in quotes but there are no commas and there has been no squeezing together.

To take the first field

STR1,JFY=(SHIFT=LEFT,LEAD=C'"',TRAIL=C'"',LENGTH=12)

shifts the data to the left, puts quotes around the string (removing trailing blanks) and makes the resulting field 12 bytes wide. (The second field involves number formatting and the third is similar to the first but with a length of 7 bytes, including the quotes.)

The OUTREC statement squeezes out all the spaces outside of the quotes (PAIR=QUOTE telling DFSORT to preserve what’s in the pair of quotes.) MID=C’,’ specifies that any run of spaces (outside of pairs of quotes) are to be replaced by a single comma.

This is, admittedly, a fairly complex example. But I hope it shows some of the capabilities of SQZ and JFY. And maybe this is a sample you can swipe and modify for your applications.

One thing that isn’t clear to me is whether trailing blanks are in fact significant in the CSV file format. Because it’s scarcely a standard it’s probably implementation-dependent. But, personally, I’d assume that blanks were significant.

Removing variable numbers of blanks could be done prior to these new functions being available but it was much more fiddly. I wouldn’t want to even attempt explaining that one. 🙂

And shortly I’ll write some tips about XML and DFSORT.

Search and Replace with the new DFSORT functional PTFs

(Originally posted 2006-04-20.)

Here’s my first tip with the new UK90006 / UK90007 DFSORT PTFs. It’s an apparently minor enhancement that I think people will like…

  • It leads to a more elegant syntactical expression.
  • It is more maintainable – in the face of changing record layouts.

Suppose you want to search a file for a string in a particular position in each record and replace all matching instances with a different string. (I might do that to scrub an SMFID in SMF records to produce sample reports.) Without DFSORT’s Symbols support (which is ancient history now) you might have to code something like…

OUTREC IFTHEN=(WHEN=(4,5,CH,EQ,C'84484'),OVERLAY=(4:C'ABCDE'))

In this example the field you are searching in is a 5-character string beginning in position 4. Hence 4 appears twice in the statement.

NOTE: I’ve already used two functions that are much more recent than the introduction of Symbols: IFTHEN and OVERLAY…

  • IFTHEN applies a BUILD (also know as FIELDs) or an OVERLAY clause to only the records that meet the IFTHEN condition. In this case
    4,5,CH,EQ,C'84484'
    specifies which field to search in.
  • OVERLAY changes only the specified fields. In this case
    4:C'ABCDE'
    overlays just the 4-byte field we previously searched in. Prior to OVERLAY you had to explicitly specify all the fields you wanted to appear in the output record, even if only one was changed – as is the case here.

Now, that works pretty well – because of these two new functions introduced with PTFs UQ95213 / UQ95214 at the end of 2004. But suppose the format of the record changes. You would have to recode all such utterances for the new field positions. It would be much better to do it in one place. And that, in a nutshell, is the case for Symbols…

Continuing with our example, you might map the record with a SYMNAMES DD that points to the following definitions…

F1,*,1,CHF2,*,1,CHSKIP,1   F3,*,5,CH

If you work through these definitions you’ll realise that F3 starts at position 4 and is of length 5 bytes. So it matches our original example. Prior to UK90006 / UK90007 you could code:

OUTREC IFTHEN=(WHEN=(F3,EQ,C'84484'),OVERLAY=(4:C'ABCDE'))
and the IFTHEN clause is fully symbolic (ignoring the literal comparison string – which could itself be converted to a symbol.)

But suppose the data was reorganised so that this field moved. You’ll notice that still requires you to change the OVERLAY clause to replace the 4 with a different position.

With UK90006 / UK90007 you can now code

OUTREC IFTHEN=(WHEN=(F3,EQ,C'84484'),OVERLAY=(F3:C'ABCDE'))

and a reorganisation of the data just means changing the Symbols deck pointed to by the SYMNAMES DD. If you’re sensible this will be in one permanent file and so it’s just one edit (plus, of course, some testing).

What has changed is the ability to specify a position (for example for OVERLAY) using a symbol.

So, I think I’ve made the maintainability point. I think this enhancement also allows more elegant expression: You now refer to the field consistently by its symbolic name, rather than in two different ways in two different places.

As I said, it’s a minor enhancement. But one I particularly like – as I mess around with DFSORT a lot and little annoys me more than clunkiness of expression and poor maintainability.

One final point: I mentioned above that you could replace the literal string values with symbols. Here’s how to do it (in this example). Add two symbol definitions to the SYMNAMES DD:

OLDVALUE,C'84484'NEWVALUE,C'ABCDE'

and replace the statement with

OUTREC IFTHEN=(WHEN=(F3,EQ,OLDVALUE),OVERLAY=(F3:NEWVALUE))

and now you can change the search and replacement string values without changing all the occurrences of these strings in all your code – just in the one place.

DFSORT New Function PTFs

(Originally posted 2006-04-20.)

As many of you know I like to beta DFSORT function PTFs. About once a year a new set of functions appears, many in direct response to customer requirements. Occasionally some of the enhancements are at my suggestion. 🙂

Yesterday PTFs UK90006 and UK90007 became available. (Being UK based it’s nice to see PTF numbers beginning with UK but I read no significance into this.) 🙂

You can read the user guide here.

I’m not going to duplicate what that document says here. But the enhancements are rather pleasing. What I will do, occasionally, is post examples of things that are easy to overlook in this announcement. As someone who’s played with the code over the past six months I think I’m well placed to do this.

And I expect Frank Yaeger (the DFSORT function developer) to be all over the web with this stuff shortly.

My New Job

(Originally posted 2006-04-07.)

As of today I have a new job.

  • I still work for IBM.
  • I’m still UK based.
  • I still do performance analysis with customers in the same way.

But some things have changed:

  • I now work in Software Group, rather than what’s now called Global Technology Services.
  • I now have a geographical territory that stretches from Rejkavik to Vladivostok, and from Stavanger to Cape Town. I would quite like to visit all of those. 🙂 Actually there’s a hole in the middle that isn’t mine. 😦 But then IBM organisation was always a little unfathomable. 🙂

So, in practical terms very little changes for those customers who already know me. In fact I might be able to be a little more responsive – in case you’re planning any unforeseen crises. 🙂 And I hope to be meeting many more (really interesting) customers in more far-flung places. I’m already told to expect to be able to do more presenting, writing and looking after user groups. So I think that’s all goodness.

So, what’s not to like? 🙂

DB2 Data Sharing Performance for Beginners

(Originally posted 2006-04-06.)

I’m strongly considering writing a DB2 Data Sharing For Beginners presentation. In fact I have a proposal in to do just such a thing for the US z/OS Conference (in Orlando in October). Obviously that would be a presentation I’d like to take to eg UKCMG and the EMEA z/OS Conference next year.

Would this be of general interest?

When I say For Beginners I’m not entirely sure I mean that. 🙂

Certainly I’d like to start simple – with such things as the kinds of structures DB2 uses and also the various configuration options an installation has. But I think I also want to talk about such things as the detailed XCF traffic that Data Sharing causes and also the effect of Coupling Facility Duplexing. (Actually if I get fancy I’d like to talk about the new RFCOM (Read For Castout Multiple) and WARM (Write And Register Multiple) Coupling Facility commands that DB2 Version 8 uses.)

As you know I like to keep feet in both (DB2 and z/OS) camps so this presentation would come at it from both angles. (It would be hard to justify just looking at Data Sharing from just the z/OS or DB2 angles.

But mainly my personal slant on this will be from the instrumentation perspective – with some nice examples to illustrate a few points.

As readers of this blog probably are my target audience…

  • Do you see this as a presentation you’d like to see someone put together?
  • What would you like to see in it?
  • Would you think a formal paper would be necessary? Or just the foils?

Another MIDAWs APAR – Might Improve Its Performance

(Originally posted 2006-04-03.)

In this entry I talked about an APAR that fixed an issue with mixed farms of control units, where only some supported MIDAWs.

Here’s another APAR. This one could increase the performance benefit of MIDAWs.

APAR OA15556 changes the way IDAWs (Indirect Data Access Words) are placed in memory, relative to the CCWs (Channel Command Words)…

(M)IDAWs point at the data for the I/O and CCWs point at chains of them. This enables scatter/gather I/O processing from disparate ranges of real storage addresses. (There’s nothing new in this paragraph.)

The point of MIDAWs is that it enables more scatter/gather with each CCW and hence improves channel efficiency.

DB2 can pass up to 64 CIs (Control Intervals) to Media Manager. When it does the CCW and all the MIDAWs cannot fit in a single page. So you get 2 CCWs and hence less effective use of the (FICON) channel. NOTE:The second CCW cannot start until the first one has finished.

So APAR OA1556 splits the MIDAWs into a separate page from the CCW so it can all be done with one CCW, improving effectiveness./

More REORG Utility Flexibility

(Originally posted 2006-03-30.)

Long ago the DB2 REORG utility traded memory for speed with SORTKEYS and SORTDATA. The latter caused the object being REORGed to be unloaded and externally sorted (eg using DFSORT). (SORTKEYS was similar but for the keys to REORG the indexes.)

This is fine if you have the memory to drive either large in-memory sorts or multiple parallel sorts. But sometimes you don’t.

APAR PK18059 for V8 introduces SORTDATA YES/NO to allow you to specify whether an individual REORG will use SORTDATA or not.