d0521a2000-02-24Martin Nilsson // This is a roxen module. Copyright © 1997-2000, Roxen IS.
f4866c1999-12-08Martin Nilsson //
e6a1712000-02-02Per Hedbor // A module for Roxen, which gives the tags
f4866c1999-12-08Martin Nilsson // <sqltable>, <sqlquery> and <sqloutput>. // // Henrik Grubbström 1997-01-12
78c9971997-02-20Henrik Grubbström (Grubba) 
52e8b62000-03-25Martin Nilsson constant cvs_version="$Id: sqltag.pike,v 1.50 2000/03/25 02:28:11 nilsson Exp $";
07bf511997-08-31Peter Bortas constant thread_safe=1;
78c9971997-02-20Henrik Grubbström (Grubba) #include <module.h> inherit "module"; inherit "roxenlib";
f4866c1999-12-08Martin Nilsson Configuration conf;
a03ea41998-09-29Johan Schön 
7a7c651999-11-05Martin Nilsson // Module interface functions
78c9971997-02-20Henrik Grubbström (Grubba) 
9424ab2000-02-05Martin Nilsson constant module_type=MODULE_PARSER|MODULE_PROVIDER; constant module_name="SQL tag module";
956ac22000-03-09Martin Stjernholm constant module_doc ="This module gives the three tags &lt;SQLQUERY&gt;, "
9424ab2000-02-05Martin Nilsson  "&lt;SQLOUTPUT&gt;, and &lt;SQLTABLE&gt;.<br>\n";
f4866c1999-12-08Martin Nilsson  TAGDOCUMENTATION #ifdef manual
5e2d652000-02-20Kenneth Johansson constant tagdoc=([
3d20d32000-02-21Kenneth Johansson "sqltable":#" <desc tag><short>
5e2d652000-02-20Kenneth Johansson  Creates an HTML or ASCII table from the results of an SQL query.
3d20d32000-02-21Kenneth Johansson </short>
5e2d652000-02-20Kenneth Johansson </desc> <attr name=ascii> Create an ASCII table rather than a HTML table. Useful for interacting with the <ref type=tag>diagram</ref> and <ref type=tag>tablify</ref> tags. </attr> <attr name=host type=database>
3d20d32000-02-21Kenneth Johansson  Which database to connect to, usually a symbolic name set in the <module>SQL Databases</module> module. If omitted the default database will be used.
5e2d652000-02-20Kenneth Johansson </attr>
3d20d32000-02-21Kenneth Johansson <attr name=SQL statement> The actual SQL-statement.
5e2d652000-02-20Kenneth Johansson </attr> <attribute name=parse> If specified, the query will be parsed by the RXML parser. Useful if you wish to dynamically build the query. </attribute>",
3d20d32000-02-21Kenneth Johansson "sqlquery":#" <desc tag><short> Executes an SQL query, but doesn't do anything with the result.</short> This is mostly used for SQL queries that change the contents of the database, for example INSERT or UPDATE.
5e2d652000-02-20Kenneth Johansson </desc> <attr name=host type=database>
3d20d32000-02-21Kenneth Johansson  Which database to connect to, usually a symbolic name set in the <module>SQL Databases</module> module. If omitted the default database will be used.
5e2d652000-02-20Kenneth Johansson </attr>
3d20d32000-02-21Kenneth Johansson <attr name=query type=SQL statement> The actual SQL-statement.
5e2d652000-02-20Kenneth Johansson </attr> <attr name=parse> If specified, the query will be parsed by the RXML parser. Useful if you wish to dynamically build the query.
3d20d32000-02-21Kenneth Johansson </attr> <attr name=mysql-insert-id type=form-variable> Set form-variable to the insert id used by Mysql for auto-incrementing columns. Note: This is only available with Mysql. </attr> "]);
f4866c1999-12-08Martin Nilsson #endif
78c9971997-02-20Henrik Grubbström (Grubba) 
d0521a2000-02-24Martin Nilsson array|object do_sql_query(string tag, mapping args, RequestID id)
78c9971997-02-20Henrik Grubbström (Grubba) {
52e8b62000-03-25Martin Nilsson  string host = query("hostname"); if (args->host) { host=args->host; args->host="CENSORED"; }
7a7c651999-11-05Martin Nilsson  if (!args->query)
d0521a2000-02-24Martin Nilsson  RXML.parse_error("No query.");
3f094d1998-08-10Per Hedbor 
7a7c651999-11-05Martin Nilsson  if (args->parse) args->query = parse_rxml(args->query, id);
961fd31997-11-29Henrik Grubbström (Grubba) 
9424ab2000-02-05Martin Nilsson  Sql.sql con;
7a7c651999-11-05Martin Nilsson  array(mapping(string:mixed)) result; function sql_connect = id->conf->sql_connect; mixed error;
961fd31997-11-29Henrik Grubbström (Grubba) 
7a7c651999-11-05Martin Nilsson  if(sql_connect) error = catch(con = sql_connect(host)); else
9424ab2000-02-05Martin Nilsson  error = catch(con = Sql.sql(lower_case(host)=="localhost"?"":host));
7a7c651999-11-05Martin Nilsson 
41dfb12000-02-25Martin Nilsson  if (error)
d0521a2000-02-24Martin Nilsson  RXML.run_error("Couldn't connect to SQL server. "+html_encode_string(error[0]));
961fd31997-11-29Henrik Grubbström (Grubba) 
7a7c651999-11-05Martin Nilsson  if (error = catch(result = tag=="sqltable"?con->big_query(args->query):con->query(args->query))) {
f8a75a1999-11-23Henrik Grubbström (Grubba)  error = html_encode_string(sprintf("Query %O failed. %s", args->query, con->error()||""));
41dfb12000-02-25Martin Nilsson  RXML.run_error(error);
78c9971997-02-20Henrik Grubbström (Grubba)  }
7a7c651999-11-05Martin Nilsson  if(tag=="sqlquery") args["dbobj"]=con; return result;
78c9971997-02-20Henrik Grubbström (Grubba) }
c7f4e51997-10-15Henrik Grubbström (Grubba) 
7a7c651999-11-05Martin Nilsson // -------------------------------- Tag handlers ------------------------------------
3f094d1998-08-10Per Hedbor 
52e8b62000-03-25Martin Nilsson string simpletag_sqloutput(string tag, mapping args, string contents, RequestID id)
7a7c651999-11-05Martin Nilsson { NOCACHE();
3f094d1998-08-10Per Hedbor 
d0521a2000-02-24Martin Nilsson  array res=do_sql_query(tag, args, id);
3f094d1998-08-10Per Hedbor 
7a7c651999-11-05Martin Nilsson  if (res && sizeof(res)) {
52e8b62000-03-25Martin Nilsson  string ret = do_output_tag(args, res, contents, id);
7a7c651999-11-05Martin Nilsson  id->misc->defines[" _ok"] = 1; // The effect of <true>, since res isn't parsed.
c7f4e51997-10-15Henrik Grubbström (Grubba) 
7a7c651999-11-05Martin Nilsson  if( args["rowinfo"] ) id->variables[args->rowinfo]=sizeof(res);
961fd31997-11-29Henrik Grubbström (Grubba) 
7a7c651999-11-05Martin Nilsson  return ret; }
961fd31997-11-29Henrik Grubbström (Grubba) 
7a7c651999-11-05Martin Nilsson  if (args["do-once"]) return do_output_tag( args, ({([])}), contents, id )+ "<true>";
78c9971997-02-20Henrik Grubbström (Grubba) 
52e8b62000-03-25Martin Nilsson  if(args->quiet) { id->misc->defines[" _ok"] = 0; return ""; }
d0521a2000-02-24Martin Nilsson  RXML.run_error("No SQL return values.");
7a7c651999-11-05Martin Nilsson }
78c9971997-02-20Henrik Grubbström (Grubba) 
9424ab2000-02-05Martin Nilsson class TagSqlplugin { inherit RXML.Tag; constant name = "emit"; constant plugin_name = "sql"; array get_dataset(mapping m, RequestID id) { array|string res=do_sql_query("sqloutput", m, id); if(m->rowinfo) id->variables[m->rowinfo] = sizeof(res); return res; } }
7a7c651999-11-05Martin Nilsson string tag_sqlquery(string tag, mapping args, RequestID id) { NOCACHE();
78c9971997-02-20Henrik Grubbström (Grubba) 
d0521a2000-02-24Martin Nilsson  array res=do_sql_query(tag, args, id);
07245f1997-09-28Henrik Grubbström (Grubba) 
7a7c651999-11-05Martin Nilsson  if(args["mysql-insert-id"]) if(args->dbobj && args->dbobj->master_sql) id->variables[args["mysql-insert-id"]] = args->dbobj->master_sql->insert_id(); else
d0521a2000-02-24Martin Nilsson  RXML.parse_error("No insert_id present.");
78c9971997-02-20Henrik Grubbström (Grubba) 
d0521a2000-02-24Martin Nilsson  return "<true />";
78c9971997-02-20Henrik Grubbström (Grubba) }
7a7c651999-11-05Martin Nilsson string tag_sqltable(string tag, mapping args, RequestID id)
07245f1997-09-28Henrik Grubbström (Grubba) {
7a7c651999-11-05Martin Nilsson  NOCACHE();
07245f1997-09-28Henrik Grubbström (Grubba) 
d0521a2000-02-24Martin Nilsson  object res=do_sql_query(tag, args, id);
0d90ed1998-01-17Henrik Grubbström (Grubba) 
7a7c651999-11-05Martin Nilsson  int ascii=!!args->ascii; string ret="";
78c9971997-02-20Henrik Grubbström (Grubba) 
7a7c651999-11-05Martin Nilsson  if (res) { string nullvalue=args->nullvalue||""; array(mixed) row;
0d90ed1998-01-17Henrik Grubbström (Grubba) 
7a7c651999-11-05Martin Nilsson  if (!ascii) { ret="<tr>"; foreach(map(res->fetch_fields(), lambda (mapping m) { return m->name; } ), string name) ret += "<th>"+name+"</th>"; ret += "</tr>\n"; }
2a9ddf1998-07-15Johan Schön 
7a7c651999-11-05Martin Nilsson  if( args["rowinfo"] ) id->variables[args->rowinfo]=res->num_rows(); while(arrayp(row=res->fetch_row())) { if (ascii) ret += row * "\t" + "\n"; else { ret += "<tr>"; foreach(row, mixed value) ret += "<td>"+(value==""?nullvalue:value)+"</td>"; ret += "</tr>\n"; } }
78c9971997-02-20Henrik Grubbström (Grubba) 
7a7c651999-11-05Martin Nilsson  if (!ascii) ret=make_container("table", args-(["host":"", "database":"", "user":"", "password":"", "query":"", "nullvalue":""]), ret);
78c9971997-02-20Henrik Grubbström (Grubba) 
7a7c651999-11-05Martin Nilsson  return ret+"<true>"; }
d0521a2000-02-24Martin Nilsson  RXML.run_error("No SQL return values.");
78c9971997-02-20Henrik Grubbström (Grubba) }
2a9ddf1998-07-15Johan Schön 
7a7c651999-11-05Martin Nilsson // ------------------- Callback functions -------------------------
2a9ddf1998-07-15Johan Schön 
9424ab2000-02-05Martin Nilsson Sql.sql sql_object(void|string host)
2a9ddf1998-07-15Johan Schön {
a03ea41998-09-29Johan Schön  string host = stringp(host)?host:query("hostname");
9424ab2000-02-05Martin Nilsson  Sql.sql con;
a03ea41998-09-29Johan Schön  function sql_connect = conf->sql_connect;
2a9ddf1998-07-15Johan Schön  mixed error;
a788ad2000-01-11Martin Stjernholm  /* Is this really a good idea? /mast
2a9ddf1998-07-15Johan Schön  error = catch(con = sql_connect(host)); if(error) return 0; return con;
a788ad2000-01-11Martin Stjernholm  */ return sql_connect(host);
2a9ddf1998-07-15Johan Schön } string query_provides() { return "sql"; }
7a7c651999-11-05Martin Nilsson  // ------------------------ Setting the defaults -------------------------
78c9971997-02-20Henrik Grubbström (Grubba)  void create() {
bc53801999-09-28Martin Stjernholm  defvar("hostname", "localhost", "Default SQL database host", TYPE_STRING, "Specifies the default host to use for SQL queries.\n" "This argument can also be used to specify which SQL server to " "use by specifying an \"SQL URL\":<ul>\n"
47d81d1997-09-05Henrik Grubbström (Grubba)  "<pre>[<i>sqlserver</i>://][[<i>user</i>][:<i>password</i>]@]" "[<i>host</i>[:<i>port</i>]]/<i>database</i></pre></ul><br>\n"
a2533f1997-09-23Henrik Grubbström (Grubba)  "Valid values for \"sqlserver\" depend on which "
bc53801999-09-28Martin Stjernholm  "SQL servers your pike has support for, but the following "
47d81d1997-09-05Henrik Grubbström (Grubba)  "might exist: msql, mysql, odbc, oracle, postgres.\n");
78c9971997-02-20Henrik Grubbström (Grubba) }
7a7c651999-11-05Martin Nilsson // --------------------- More interface functions --------------------------
12ac991997-11-26Henrik Grubbström (Grubba)  void start(int level, object _conf)
78c9971997-02-20Henrik Grubbström (Grubba) {
52e8b62000-03-25Martin Nilsson  if (_conf)
12ac991997-11-26Henrik Grubbström (Grubba)  conf = _conf;
78c9971997-02-20Henrik Grubbström (Grubba) } string status() {
a788ad2000-01-11Martin Stjernholm  if (mixed err = catch {
12ac991997-11-26Henrik Grubbström (Grubba)  object o;
7a7c651999-11-05Martin Nilsson  if (conf->sql_connect)
12ac991997-11-26Henrik Grubbström (Grubba)  o = conf->sql_connect(QUERY(hostname));
7a7c651999-11-05Martin Nilsson  else
52e8b62000-03-25Martin Nilsson  o = Sql.sql(QUERY(hostname)); return(sprintf("Connected to %s server on %s<br />\n",
fa40051997-06-20Henrik Grubbström (Grubba)  o->server_info(), o->host_info()));
7a7c651999-11-05Martin Nilsson  })
a788ad2000-01-11Martin Stjernholm  return
52e8b62000-03-25Martin Nilsson  "<font color=\"red\">Not connected:</font> " + replace (html_encode_string (describe_error(err)), "\n", "<br />\n") + "<br />\n";
78c9971997-02-20Henrik Grubbström (Grubba) }