Manual for mod_mysql_include by Sasha Pachev <sasha@mysql.com>

INTRODUCTION

The reason I wrote mod_mysql_include originally was the need for a banner-rotation 
system on http://www.mysql.com . I wanted to have a straightforward way to directly
insert the output of a MySQL query into HTML while causing minimum possible load on 
the server, and for a problem like that Apache module is about as fast as you can go.

mod_mysql_include directly forwards the contents of the parsed files to the client with
the exception of the code enclosed in <+ +> , which is treated as mod_mysql_include 
directives.

SYNTAX

mod_mysql_include directives follow the following syntax:

<+directive-name param-name="param-val" +>

white space and newlines serve as delimiters, and you can have as much of it as you want

The parameter value in quotes will be expanded the Perl/PHP way -- $varname will be
 replaced with the value of the internal module variable with the name of varname.


MODULE VARIABLES

mod_mysql_include supports internal module variables. A variable can be set with var-set,
 echoed with var-echo, and referenced inside a quoted paramenter value string with $varname,
like in Perl or PHP. The legal characters for a variable name are [0-9a-zA-Z\-\.\[\]] --
 any digit or letter, dot, dash, and []


See the docs below on var-set and
 var-echo for more info.


PARSER DIRECTIVES

Name: var-set
Parameters: name,value
Description: Sets the internal module variable specified by <name> to the value specified
 by <value>
Errors: if malloc() fails, writes a notice to Apache error log and does nothing
Example: <+var-set name="first-name" value="Sasha"+>

Name: var-echo
Parameters: name
Description: Echoes the value of the variable to the client
Errors: if the variable had not been initialized, echoes NULL to the client
Example: <+var-echo name="first-name"+>

Name: mysql-include
Parameters: query,name,print-cols

Description: executes <query>, if <print-cols> is not specified, all the colums/rows of the results
 are echoed to the client concatenated a row at a time. If <print-cols> is specified,
 only the columns that are listed will be echoed. The list must consist of ,(comma)
 delimited integers with no whitespace in between. The integers are 0-based column offsets
 (numbers). You should use this directive for SELECT and other result set returning queries

 If <name> is given, the result will be saved according to the following scheme:

 Each column is stored in a module variable with the name of <name>.<column-name>[<row-num>]

   <column-name> is the column name as returned by mysql_list_fields()
   <row-num> is the 0-based row offset (number)

Errors: if the query fails, will echo the string specified by MySQLIncludeDefaultHTML
Apache run-time directive -- see mod_mysql_include Apache Run-Time Directives

Example: <+mysql-include query="select html, id,
   impressions,clicks from banner where max_impressions > impressions and max_clicks > clicks order by 
      last_shown limit 1  " 
    name="banner" print-cols="0" +>



Name: mysql-run
Parameters: query
Description: executes the query with a call to mysql_query() and ignores the result set if 
 there is any. You should use it for INSERT, UPDATE and other non result set returning
 queries
Errors: if the query fails, echoes the value of MySQLIncludeWarnFailedQueryHTML Apache
directive to the client

Example:  <+mysql-run query="update banner set impressions = impressions + 1,
      last_shown = current_timestamp where id = $banner.id[0]"+>


APACHE RUN-TIME DIRECTIVES

mod_mysql_include extends Apache to have the following configuration directives:

Mandatory directives:

MySQLIncludeUser "username"  -- mysql user mod_mysql_include will connect as
MySQLIncludePass "pass" -- mysql password for the connection
MySQLIncludeHost "host" -- the host where MySQL is running
MySQLIncludeDB "db"    -- the default database

Optional directives:

MySQLIncludeDefaultHTML "htmlstuff"   -- the html to show if the query in mysql-include
 fails  -- compiled in default is "<center><A href=\"https://www.mysql.com/license.htmy\">\
<img border=0 src=\"http://www.mysql.com/images/powered_by_mysql.gif\" width=\"88\"\
height=\"46\" alt=\"Powered by MySQL\"></a></center>"

MySQLIncludeWarnFailedQueryHTML "warninghtml"  -- the html to show if the query in
 mysql-run fails -- compiled defaults is "<font color=red>mysql query failed -- details in \
   the error log</font>"


TECHNICAL DETAILS FOR THE CURIOUS

If you are really that curious, you should read the source. However, if you are too lazy
to read the source, or just do not have the time, or maybe have not learned C yet (you 
 should, if that's the case :-) ),  here are some bits of info:

- the parsing is done with a LEX-generated token scanner
- the variables are stored in a linked list -- memory management is done with
  malloc()/free() directly -- decided not to use the server pool because you cannot 
  free() -- and I just needed to :-) I know, using a hash would be more efficient, but
  I just did not feel ambitions enough to either integrate with some hash code or write
  my own -- maybe I'll do it in the future

- connections to MySQL are persistent. If the server gone away error is discovered during
  a query, reconnection is attempted. If it fails, the bad_conn flag is set, and connections
  will not be reattempted during the lifetime of the child -- most of the time this is
  a reasonable behaviour, because if the connection fail once, it will fail again unless
  something gets fixed in the MySQL server or httpd.conf -- if such a fix is in order,
  sending a HUP to httpd is a good idea anyway -- if you disagree, give me your reasons,
  and I might add a configuration option to be able to alter this behaviour.


TO DO

  - test it under high load, make sure there are no memory leaks ( pretty sure there
   are not any, I did not notice increased memory usage overtime, and was very memory 
   conscious when coding, but you never know :-). Another issue that may reveal itself
   under high load is inconsistent behaviour on incomplete requests (eg. when the user
   hits the Stop button). I tried this a few times manually, and could not make it crash,
   however, it would be a lot better to do this through some load simulator that tries 
   weird stuff. I do ap_block_alarms around critical sections, however I may have missed
   something important

  - add branching directives <+if+> <+else+> <+endif+> and loops
  - set an error status if a query fails -- will only make sense if there is branching
  - implement <+import-get-vars+> <+import-post-vars+>
  - implement <+include+>
  - anything else you can think of :-)

BUGS

  I took out all that I found. If you find any, which will definitely happen 
   (hey, have you ever seen version 1.0 without a bug :-) ), e-mail me at sasha@mysql.com
   with as much detail as possible -- focus on making it easy for me to repeat the problem
   -- the better you do this, the faster I'll fix it. Or, if you want to fix it yourself and
   send me the patch, I will not object :-)
