SqlHtmlRpt.pm

Uses an HTML template file containing embedded SQL statements and optional CGI parameters to run and format a report. Supports nested queries, so you can do breaks and rollups.

It works with mod_perl and Perl versions 5.5.3 and later. It has been used extensively with MySQL, but should work with any SQL database supporting the DBI:: interface.

Copyright

Author
Fred Morris

version
2.8

Creation Date
07-Jul-2002

Modification History
FWM 12-Jun-2005 parse(); Check for whitespace after the comment opener in comment mode.

FWM 11-Jun-2005 format(), subclassable methods; No functional changes. Refactored format() and created subclassable methods, for easier.. subclassing. properties; The template is now stored as a property.

FWM 17-Aug-2003 format() $cgi and $dbi became non-optional with 2.0. Shame on me...

FWM 12-May-2003 query() If a field is undefined, set it to an empty string. This gets rid of unitialized value errors in the logs.

FWM 08-Mar-2003 ... Implement nested/iterative queries. format() opt (a hashref containing additional substitutions) as an optional parameter was removed. This is easy for the caller to do for themselves, and they get more control that way anyway.

Copyright
Copyright (c) 2002-2005 by Fred Morris, 6739 3rd NW Seattle WA USA 98117 e-mail: m3047 (funny sign) inwa-dot-net telephone 206.297.6344

Licensed under the same terms as Perl itself, with no warranty or claims of fitness for any particular use.

Acknowledgements
Thanks to early adopter Daniel Stillwaggon for the valuable feedback.

Properties

The following properties may exist for the object:

cgi
This is an instance of CGI, and is supplied in the call to new().

dbh
This is a DBI:: database handle, and is supplied in the called to new().

formatter
A reference to a procedure which will be called for each field in the result set, prior to substituting it into the template.

errstr
This is an error string containing a description of the last error. If there is no error it tests false (usually undef).

next
A reference to the next SqlHtmlRpt object at this level

subq
A reference to the first SqlHtmlRpt object which is a subquery attached to this object.

presql
The portion parsed which lies before the SQL statement.

sql
The SQL statement.

postsql
The portion parsed which lies after the SQL statement but before the list definition.

list
The list definition.

tail
Everything after the list definition.

template
At the outer level, the template is available as a property within format().

Subclassable Methods

The methods here are called by format(). They have been structured to make it easy to subclass and extend SqlHtmlRpt. All routines are expected to return undef on success or an error message on failure unless otherwise noted.

fmt_init( init )

Called prior to CGI parameter processing. The init flag is provided for extensions. This method does nothing here.

fmt_param( field_hash, init )

Processes CGI parameters into field_hash to be used as substitutions.

init indicates that the report is being called in initialization mode and that valid parameters cannot be expected. Extensions may want to do something special with this.

One global check is performed, which is that a bare quotation mark is disallowed since this breaks most INPUT tags in HTML forms.

The following parameters are supported. If an error is found they should return undef and store a description in the errstr property.

fmt_param_n( param_value )

Handles numeric parameters.

fmt_param_s( param_value )

Handles string parameters. Basically nothing to do here, escaping is performed later during the actual substitutions.

fmt_param_p( param_value )

Handles unchecked, raw parameters. Unsafe for substitution into SQL statements unless you do some additional checking yourself before calling SqlHtmlRpt.

fmt_report( template, field_hash )

Prepares a report from template, using the substitutions in field_hash.

This routine should return the formatted report. If an error is encountered it should return undef and store a description in the errstr property.

Public Methods

new( cgi, dbh, formatter )

Instantiates a SqlHtmlRpt Object.

cgi (optional)
A handle to a CGI:: object from which parameters can be retrieved. See the CGI Parameters section for a description of the parameters that the object looks for and how it uses them. If only an initial call is being performed, then it isn't necessary.

dbh (optional)
A DBI:: database handle which the object can use to perform the SQL query. If only an initial call is being performed, then it isn't necessary.

formatter (optional)
A callback which accepts the field as a string and performs appropriate filtering on it and returns it as a string. Such a formatter would typically escape characters and sequences which might be confused with HTML language elements. If not supplied, then values are substituted as returned by the query.

disconnect()

Calls disconnect() on the passed dbh.

format( sql_mode, template, init )

Uses a SQL statement embedded in an HTML template to query the database and perform substitutions into the template to format the returned query set. $dbh->selectall_arrayref() is used to retrieve the query set. Certain CGI parameters can be used in the SQL statement; see the CGI Parameters section and the SQL Statement section. Substitutions performed into the template are described in the Templates section. Also, see the Usage Notes section for an overview of how this widget should be used and how it works.

If an error is encountered, this routine should return undef and leave an error message in errstr.

sql_mode
This can be either 'tag' or 'comment' and controls how the routine looks for the SQL statement within the template:
tag
Looks for the statement surrounded by <SQL> and </SQL>. For example:

<SQL>SELECT * From Foo</SQL>

comment
Looks for the statement in an HTML comment of the form <!--SQL -->. For example:

<!--SQL SELECT * From Foo -->

template
An HTML template, passed in as a string.

init (optional)
If supplied and true, then no query is performed, and substitution is performed as though the query returned no records. More useful than it sounds, see the Usage Notes section.

CGI Parameters

Certain substitutions will be performed on the SQL statement based on CGI parameters. See the SQL Statement section as well. format() looks for CGI parameters starting with sqlx1 and continuing until it finds one which is undefined, where x is n, s, or p. Only one parameter with a given number is processed, for example sqln1, sqln2, sqls3, sqln4. It substitutes the corresponding values for instances of %%sqlxn%%.

sqln1..n
Substitutes the corresponding values for instances of %%sqln1%% etcetera after validating that the supplied values are numeric.

sqls1..n
Similar to the preceding, except the parameter value is subjected to database quotation prior to insertion within the SQL statement(s). If inserted elsewhere it is not quoted.

sqlp1..n
Similar to the preceding, except the parameter value is inserted raw. Be careful with this one unless you trust your (ab)users.

SQL Statement

The SQL statement is presumed to be a SELECT statement. As noted, substitutions can be performed into it with a one-to-one correspondence between CGI parameters of the form sqlx1..n and cliches within the SQL statement of the form %%sqlx1..n%%.

Templates

Insertions occur into the template according to the following syntax. The syntax of a substitution tag is %%tag%%.

sqlx1..n
For each corresponding CGI parameter, the found value will be reinserted back into the template. sqlsn parameters will be subjected to database quotation before being inserted into the SQL statement. If inserted elsewhere in the template they are not quoted.

liststart, listend
These tags can occur raw or enclosed like HTML tags. Brackets a list of records. The form for nested subqueries is liststartn where n starts at 1 for the first level of subqueries.

The following parameters are repeated for each record.

sqlf1..n
For each column in the result set, a substitution of this form is performed. The numbering is independent of the numbering for CGI parameters. Substitutions of fields from subqueries are of the form sqlfx_n, where x is 1 for the first level of subqueries, and so on.

Fields from outer queries are available for substitution into subqueries.

Usage Notes

Typical usage is to create an HTML template with several elements.

This object is generally called from some other enclosing CGI handler. This handler is designed to be called initially, and when it sees that this is the case it calls SqlHtmlRpt->format( ..., ..., 1 ), that is with init set to true. What ends up happening is that the user sees a blank (or defaulted) form and an empty result set.

At this point the user fills in query information and clicks the submit button. This time the enclosing handler sees that this is not an initial call. It validates the user (and perhaps the CGI parameters), and allocates a database handle. It then calls SqlHtmlRpt->format( ... ).

format() now scans the template to locate (and strip) the SQL statement. It then iterates over sqlx1..n until it finds an undef and performs the appropriate substitutions into the SQL statement. It also goes back to the template and substitutes the values back in as desired, so that the user will see the values that she entered when the result set is returned.

It then calls dbh->selectall_arrayref() to query the database. Provided the result status indicates success, clones everything between the liststart and listend tags and substitutes columns for the appropriate sqlf1..n tags, and repeats this for every row in the array.

You should find a sample report template accompanying this module.

A Message From Our Sponsor

Want help? Fred Morris has been a licensed business in Seattle Washington since 1984. I have extensive experience writing and using reporting tools. I helped write Park Software's XENTIS report writer for OpenVMS (and the mother of all report wizards), working on its development off and on for about 8 years. Not only have I used XENTIS in production environments, I have extensive experience with a variety of SQL (and other) databases as well as with ACI's 4th Dimension and Microsoft's Access.

We can provide training, consulting and even do it all for you: contact us!