This post continues yesterday's theme of doing things to files in the IFS with SQL. Then it was how to read an IFS file, today will be the opposite writing to an IFS file.
This was added in the latest round of Technology Refreshes, IBM i 7.4 TR3 and 7.4 TR3. Prior to these TRs I had used C APIs embedded in a RPG program. Having played with this this is so much easier using these new SQL procedures.
As with the IFS read table function there are three different SQL procedures to write to an IFS file:
- IFS_WRITE: Write plain text to the IFS file
- IFS_WRITE_UTF8: Write UTF8 text
- IFS_WRITE_BINARY: Write binary text
All of these procedure have the same parameters:
- PATH_NAME: Path name to the file that will be written to. It is not case sensitive
- LINE: Data to be written to the file
- FILE_CCSID: CCSID used when writing to a file. If I am writing to an existing file this is ignored. If IFS_WRITE_UTF8 is used the CCSID will default to 1208, for the others it will be the partition's default
- OVERWRITE: Three values:
- APPEND If the file in the path name exists add to the end of the file
- NONE If the file already exists nothing happens
- REPLACE An existing file is replaced, deleted and a new one created
- END_OF_LINE: Is not supported if IFS_WRITE_BINARY is used. The others will accept:
- CR Carriage return
- CRLF Carriage return and line feed
- LF Line feed
- LFCR Line feed and carriage return
- NONE No end of line character(s)
I am going to start by using the file I was reading yesterday: test.txt. When I use the IFS_READ table function it shows the file's contents:
SELECT * FROM TABLE(QSYS2.IFS_READ('/home/MyFolder/test.txt')) LINE NUMBER LINE ----------- ------------- 1 First line 2 Second line 3 Third line 4 Fourth line 5 Fifth line |
Let me write a line to that file:
CALL QSYS2.IFS_WRITE('/home/MyFolder/test.txt', 'Added line 1', OVERWRITE => 'APPEND') |
I read the file to see what happened.
LINE NUMBER LINE ----------- ------------- 1 First line 2 Second line 3 Third line 4 Fourth line 5 Fifth lineAdded line 1 |
The line was appended to the file, but not as a new line, just as a continuation of the last line.
I deleted the file test.txt, and uploaded saved copy from my PC to the IFS folder. Let me try to write again this time with the end of line character CRLF.
CALL QSYS2.IFS_WRITE('/home/MyFolder/test.txt', 'Added line 2', OVERWRITE => 'APPEND', END_OF_LINE => 'CRLF') |
Alas, the same happened to the file as before. The line was appended to the end of the last line:
LINE NUMBER LINE ----------- ------------- 1 First line 2 Second line 3 Third line 4 Fourth line 5 Fifth lineAdded line 2 |
This must mean that the fifth line does not end with a character that IFS_WRITE understands as an end of line character. So I need to be a bit creative. Let me write a line to the file with just a CRLF character, and then write the line I want to add to the file. Again I replace the file with my saved copy.
01 CALL QSYS2.IFS_WRITE('/home/MyFolder/test.txt', '', OVERWRITE => 'APPEND', END_OF_LINE => 'CRLF') ; 02 CALL QSYS2.IFS_WRITE('/home/MyFolder/test.txt', 'Added line 3', OVERWRITE => 'APPEND', END_OF_LINE => 'CRLF') ; |
In the first statement I am writing a null value to the file, which is two apostrophes next to each other ( '' ).
Now I get the results I want, as the first statement just added a CRLF to the fifth line:
LINE NUMBER LINE ----------- ------------- 1 First line 2 Second line 3 Third line 4 Fourth line 5 Fifth line 6 Added line 3 |
Providing I have the end of line parameter in my statement I can keep adding line to my file.
CALL QSYS2.IFS_WRITE('/home/MyFolder/test.txt', 'Added line 4', END_OF_LINE => 'CRLF') |
The overwrite parameter is optional, its default is to append a new line to the file. Which gives me:
LINE NUMBER LINE ----------- ------------- 1 First line 2 Second line 3 Third line 4 Fourth line 5 Fifth line 6 Added line 3 7 Added line 4 |
This time I am going to use the IFS_WRITE_UTF8 procedure.
CALL QSYS2.IFS_WRITE_UTF8('/home/MyFolder/test.txt', 'Added line UTF8', END_OF_LINE => 'CRLF') |
I am still going to use the IFS_READ table function to display the file's contents.
LINE NUMBER LINE ----------- ------------- 1 First line 2 Second line 3 Third line 4 Fourth line 5 Fifth line 6 Added line 3 7 Added line 4 8 Added line UTF8 |
As the file was created as a plain text file, not as UTF8, the line is added with the file's CCSID which results in a plain text line.
What would happen if I used the IFS_WRITE_BINARY procedure?
CALL QSYS2.IFS_WRITE_BINARY('/home/MyFolder/test.txt', 'Added line binary', END_OF_LINE => 'CRLF') |
I get an error:
SQL State: 07006 Vendor Code: -99999 Message: Data type mismatch. (class java.lang.NumberFormatException) |
I am not worried about this as I cannot think of a scenario where I would need to write a binary string to a plain text file.
As I mentioned previously if I wanted to replace the contents of the existing file I would just use REPLACE in the overwrite parameter.
CALL QSYS2.IFS_WRITE('/home/MyFolder/test.txt', 'Replace line', OVERWRITE => 'REPLACE', END_OF_LINE => 'CRLF') |
The existing file is deleted and replaced by new one which contains the new line.
LINE NUMBER LINE ----------- ------------- 1 Replace line |
If I have a file name that does not exist in the folder given in the path name this procedure will create it.
CALL QSYS2.IFS_WRITE('/home/MyFolder/new_test.txt', 'New file and a new line', END_OF_LINE => 'CRLF') |
I can use the IFS_OBJECT_STATISTICS table function to confirm that the file was created:
SELECT PATH_NAME,OBJECT_TYPE,CREATE_TIMESTAMP FROM TABLE (QSYS2.IFS_OBJECT_STATISTICS('/home/MyFolder')) PATH_NAME OBJECT_TYPE ------------------------- ----------- /home/MyFolder *DIR /home/MyFolder/new_test.txt *STMF /home/MyFolder/test.txt *STMF |
And see the contents of the new file using the IFS_READ:
SELECT * FROM TABLE(QSYS2.IFS_READ('/home/MyFolder/new_test.txt')) LINE NUMBER LINE ----------- ----------------------- 1 New file and a new line |
Have to say I like this as it this so much easier than using the C APIs called from a RPG program.
You can learn more about the IFS_WRITE SQL procedures from the IBM website here.
This article was written for IBM i 7.4 TR3 and 7.3 TR9.
OMG! I wish I had these a few years ago. Would have made a project so much easier!
ReplyDeleteTotally amazed.
ReplyDeleteHi,
ReplyDeletefirst at all thank you for the blog. Help me a lot.
I also played arround with IFS_READ/WRITE.
When using FILE_CCSID (e.g. 1208) it has same effect like END_OF_LINE => 'CRLF'. A new line will be created when OVERWRITE=APPEND
Very cool Simon!
ReplyDeleteGreat stuff as always Simon!
ReplyDeleteGood to know and I am glad to see these enhancements.
ReplyDeleteAmazing! Thank you so much for this
ReplyDeletecool Simon, yeah really handy and lot shorter than using C or IFS api.
ReplyDeleteOnly one note for those, like me, want to use this simple way for write IFS in a RPG Program.
ReplyDeleteWhen you use a variable for the PATH of the document and a variable for the data to write in the document, be sure to define both like a VARCHAR variable.
If not and the variable lenght is greater that data contained, you have a problem to read the file.
EXAMPLE :
dcl-s doc_data varchar(1000);
dcl-s doc_path varchar(100);
// Write in IFS
exec sql
CALL QSYS2.IFS_WRITE(PATH_NAME => :doc_path,
LINE => doc_data,
END_OF_LINE => 'CRLF',
OVERWRITE => 'APPEND',
FILE_CCSID => '819');
P.S. For Simon
Me too i'm using only TOTAL FREE from when it's released
Hi Simon
ReplyDeleteThis is really great to see. Could you please clarify how this SQL procedure compares to commands like CPYFRMIMPF?
The only way I can answer that is: it depends.
DeleteSome of it is personal preference. It could the be standards of where you work. It could be the IBM i security settings.
Personally this will not replace me created CSV using the CPYTOIMPF command.
I knew something like this would happen, were still on 7.3! ..on stand by
ReplyDeleteIf you get the latest PTFs, especially the database ones, for 7.3 you will get this too.
DeleteGreat stuff, I was thinking of a way to create/write the IFS file a batch of records, let's say a select that writes the data in CSV. I now I can use a cursor adn loop it appending lines, just curious if I could do it in 1 step?
ReplyDeleteHi Simon, how can I read a phisical file and for each records, use the IFS_WRITE Funtion to write the file? Best Regards.
ReplyDeleteThere is a future post that describes how to do this.
DeleteHello, nice Post. I need help with someting.... i Cant use variable in path_name=> :variable1
ReplyDeletevariable is varchar(34)
But gives me error...
Sorry for bad English.
It worked for me when I used a variable in the path name:
Deletedcl-s PathName varchar(100) ;
PathName = '/home/MyFolder/test_1.txt' ;
exec sql CALL QSYS2.IFS_WRITE(:PathName,'Test line',
END_OF_LINE => 'CRLF') ;