Simple email validation SQL RPG ILE program

IBM i

Jun 13

There are lots of code snippets, example programs and long waffling discussions in internet land filled with pointy headed AS400, iSeries and IBM i programmer chaps (and chappettes) arguing about the best way to “validate an email address in RPG”. I know because I’ve read nearly all of them, as well as tried all the techniques. But yesterday I stumbled across a really simple variation of an established technique.

Using Regex to validate an email

Look at this SQLRPGLE code snippet:

exec sql
    set :myCount = regexp_count( :myEmail, '^(?:\w+\.?)*\w+@(?:\w+\.)*\w+$' );

If myCount = 1;
  // email is good
else;
  // email is malformed
endif;

Pretty simple huh!

So , what are we doing with this regular expression?

The regular expression ‘^(?:\w+\.?)*\w+@(?:\w+\.)*\w+$‘ is saying – check this input string meets the standard of something before an @ sign followed by something dot something:

The internet is filled with alternate regular expressions to make the email validation logic as complex as you wish.

For my purposes this simple logic is exactly what I need.

So, let’s plumb this into a an example RPG program, wrapped in a tidy sub-procedure, with a sprinkling of comments, my favorite QUILNGTX *API for display screen messages without coding any DSPF and using variable length characters –  just for fun (Yes, I know, I need to get out more):

// --------------------------------------------------------------------
RPG Code Snippet// Example SQL RPG program : VLD8EMAIL
// Author : Nick Litten
// ? CRTSQLRPGI ??OBJ(LITTENN/VLD8EMAIL)
//   SRCFILE(LITTENN/QSQLSRC)
//   SRCMBR(VLD8EMAIL)
//   OBJTYPE(*PGM)
// History:
// 2018-06-12 NJL Created
// --------------------------------------------------------------------
// This program accepts an input parameter of 'email address'.
// We validate it using a regular expression in SQL and
// display a message in a window telling you if the email
// address is good or bad. nice or naughty.
// Simple but Saucy.
// --------------------------------------------------------------------

ctl-opt dftactgrp(*no) option(*nodebugio:*srcstmt) ;

dcl-pi *n; // this is our program input a 255 long variable email address
  email varchar(255);
end-pi;

dcl-pr email_is_valid IND; // this is our subprocedure - put this in a *SRVPGM to share it
  *n like(email);
end-pr;

if email_is_valid(email);
  display_window ('Email Address ' + %trim(email) + ' is valid');
else;
  display_window ('Yuk! Email Address ' + %trim(email) +
  ' is very naughty indeed. Bad Email! ');
endif;

*inlr = *on;
return;


// --------------------------------------------------------------------
// PROCEDURE: email_is_valid
// --------------------------------------------------------------------
// Accept an input field (the email address) and validate it using
// a regular expression to perform basic email address syntax checks.
//
// REGEXP_COUNT Returns a returns a count of the number of times
// that a regular expression pattern is matched in a string
//
// The leading ^ and trailing $ match the beginning and the ending of
// the input string, respectively. That is, the entire input string
// shall match with this regexe, instead of a part of the input string.
//
// \w+ matches 1 or more word characters (a-z, A-Z, 0-9 and underscore).
//
// [.-] matches character . or -. We need to use . to represent . as . has
// special meaning in regexe. The \ is known as the escape code, which
// restore the original literal meaning of the following character.
//
// [.-]? matches 0 or 1 occurrence of [.-].
//
// ([.-]?\w+)* matches 0 or more occurrences of [.-]?\w+.
//
// The sub-expression \w+([.-]?\w+)* is used to match the username in
// the email, before the @ sign. It begins with at least one word
// character (a-z, A-Z, 0-9 and underscore), followed by more word
// characters or . or -. However, a . or - must follow by a word
// character (a-z, A-Z, 0-9 and underscore). That is, the string cannot
// contain "..", "--", ".-" or "-.". Example of valid string are "a.1-2-3".
//
// The @ matches itself.
//
// The sub-expression .\w{2,3} matches a . followed by two or three word
// characters, e.g., ".com", ".edu", ".us", ".uk", ".co".
//
// (.\w{2,3})+ specifies that the above sub-expression shall occur one or
// more times, e.g., ".com", ".co.uk", ".edu.sg" etc.
// --------------------------------------------------------------------

dcl-proc email_is_valid;
dcl-pi *n IND;
  myEmail like(email);
end-pi;
dcl-s myCount uns(10);

// you can change this Regex string to anything you like!!!
dcl-s myEmailValidationRegex varchar(100)
      inz('^(?:\w+\.?)*\w+@(?:\w+\.)*\w+$');

exec sql
  set :myCount = regexp_count(:myEmail, :myEmailValidationRegex);

If myCount = 1;
  return *on;
else;
  return *off;
endif;
end-proc;


// --------------------------------------------------------------------
// PROCEDURE: display_window
// --------------------------------------------------------------------
// Use IBM i *API 'QUILNGTX' to dynamicalluy display messages in a
// window format rather than on the error message line
// --------------------------------------------------------------------

dcl-proc display_window export;
dcl-pi display_window;
  p_Text varchar(8192) const;
  p_MsgId char(7) Options(*nopass: *omit);
  p_MsgFile char(21) Options(*nopass: *omit);
end-pi;
dcl-ds myApiError inz qualified;
  Bytes int(10) inz(%size(myApiError));
  BytesAvailable int(10) inz;
  ErrorID char(7) inz;
  Reserved char(1) inz(x'00');
  MessageData char(128) inz;
end-ds;
dcl-pr QUILNGTX extpgm('QUILNGTX');
  *n char(8192) const;
  *n int(10) const;
  *n char(7) const;
  *n char(21) const;
  *n options(*omit: *varsize)
like(myapierror);
end-pr;
dcl-s myMsgId like(p_MsgId);
dcl-s myMsgFile like(p_MsgFile);
If %Parms = 1;
  myMsgId = 'CPF9999';
  myMsgFile = 'QCPFMSG';
Elseif %Parms = 2;
  myMsgId = p_MsgId;
  myMsgFile = 'QCPFMSG';
Elseif %Parms = 3;
  myMsgId = p_MsgId;
  myMsgFile = p_MsgFile;
Endif ;
QUILNGTX ( p_Text :
  %Len(p_Text) :
  myMsgId :
  myMsgFile :
  myApiError );
end-proc;

I hope that helps someone out?

Testing is easy – I just knocked together a little calling program, because calling variable length input parameters from an IBM i command line is painful:

dcl-pr tst_email extpgm('VLD8EMAIL'); 
*n varchar(255); 
end-pr; 

dcl-s email varchar(255); 

email = 'nick@nicklitten.com'; 
tst_email(email); 

email = 'nick@this-is_a@very$duff email'; 
tst_email(email); 

*inlr = *on;

and when I call my TEST program I see these two sceeens:

PS: to test your regular expressions check out http://emailregex.com/regex-visual-tester/ it’s excellent!

Follow

About the Author

IBM i Software Developer, Digital Dad, AS400 Anarchist, RPG Modernizer, Alpha Nerd and Passionate Eater of Cheese and Biscuits. Nick Litten Dot Com is a mixture of blog posts that can be sometimes serious, frequently playful and probably down-right pointless all in the space of a day. Enjoy your stay, feel free to comment and in the words of the most interesting man in the world: Stay thirsty my friend.