Index: /main/trunk/greenstone2/runtime-src/src/recpt/Makefile.in
===================================================================
--- /main/trunk/greenstone2/runtime-src/src/recpt/Makefile.in	(revision 22045)
+++ /main/trunk/greenstone2/runtime-src/src/recpt/Makefile.in	(revision 22046)
@@ -93,8 +93,19 @@
 USE_SQLITE = @USE_SQLITE@
 ifeq ($(USE_SQLITE), 1)
+SQLITE_DEFINES = -DUSE_SQLITE
 SQLITE_LIBS = $(COMMON_PACKAGES_DIR)/sqlite/lib/libsqlite3.a -ldl -lpthread
 else
+SQLITE_DEFINES =
 SQLITE_LIBS = 
 endif
+
+ifeq ($(USE_SQLITE), 1)
+SQL_SOURCES = sqlqueryaction.cpp
+SQL_OBJECTS = sqlqueryaction.o
+else
+SQL_SOURCES =
+SQL_OBJECTS =
+endif
+
 
 USE_Z3950 = @USE_Z3950@
@@ -135,5 +146,5 @@
 CXX = @CXX@
 CXXFLAGS = @CXXFLAGS@ @COMPAT32BITFLAGS@
-DEFS = @DEFS@ -DNZDL -DQUIET -DSHORT_SUFFIX -DPARADOCNUM -DHAVE_CONFIG_H $(FASTCGI_DEFS) $(GDBM_DEFINES) $(Z3950_DEFS)
+DEFS = @DEFS@ -DNZDL -DQUIET -DSHORT_SUFFIX -DPARADOCNUM -DHAVE_CONFIG_H $(FASTCGI_DEFS) $(GDBM_DEFINES) $(SQLITE_DEFINES) $(Z3950_DEFS)
 INCLUDES = -I. -I$(GSDL_DIR) -I$(COMMON_DIR)/src/lib \
         -I$(COLSERVR_DIR) -I$(PROTOCOL_DIR) \
@@ -186,5 +197,7 @@
 	phindbrowserclass.cpp \
 	pingaction.cpp \
+	basequeryaction.cpp \
 	queryaction.cpp \
+	$(SQL_SOURCES) \
 	querytools.cpp \
 	receptionist.cpp \
@@ -231,5 +244,7 @@
 	phindbrowserclass.o \
 	pingaction.o \
+	basequeryaction.o \
 	queryaction.o \
+	$(SQL_OBJECTS) \
 	querytools.o \
 	receptionist.o \
Index: /main/trunk/greenstone2/runtime-src/src/recpt/formattools.cpp
===================================================================
--- /main/trunk/greenstone2/runtime-src/src/recpt/formattools.cpp	(revision 22045)
+++ /main/trunk/greenstone2/runtime-src/src/recpt/formattools.cpp	(revision 22046)
@@ -1265,4 +1265,5 @@
   
     ResultDocInfo_t& trav_docinfo = trav_response.docInfo[0];
+
     // use this for rest of routine
     docinfo = trav_docinfo;
@@ -1281,5 +1282,5 @@
     here++;
     if (*(oid.begin()) == '"') translate_parent (oid, docinfo.OID);
-    
+
     //get the information associated with the metadata for child doc
     if (!get_info (oid, collection, "", child_metadata, 
@@ -1296,9 +1297,13 @@
     text_t child_metavalue 
       = get_formatted_meta_text(child_docinfo.OID,metaname_rec,meta,siblings_values);
+
+
     if (!first) result += child_field;
     first = false;
     // need to do this here cos otherwise we are in the wrong document
-    result +=  expand_metadata(child_metavalue,collection,collectproto,
-			       child_docinfo,disp,options,logout);
+    text_t em =  expand_metadata(child_metavalue,collection,collectproto,
+				 child_docinfo,disp,options,logout);
+
+    result += em;
   }
   return result;
Index: /main/trunk/greenstone2/runtime-src/src/recpt/librarymain.cpp
===================================================================
--- /main/trunk/greenstone2/runtime-src/src/recpt/librarymain.cpp	(revision 22045)
+++ /main/trunk/greenstone2/runtime-src/src/recpt/librarymain.cpp	(revision 22046)
@@ -49,4 +49,9 @@
 #include "pingaction.h"
 #include "queryaction.h"
+
+#if defined(USE_SQLITE)
+#include "sqlqueryaction.h"
+#endif
+
 #include "tipaction.h"
 #include "statusaction.h"
@@ -141,5 +146,11 @@
   aqueryaction->set_receptionist (&recpt);
   recpt.add_action (aqueryaction);
-		
+
+#if defined(USE_SQLITE)
+  sqlqueryaction *asqlqueryaction = new sqlqueryaction();
+  asqlqueryaction->set_receptionist (&recpt);
+  recpt.add_action (asqlqueryaction);
+#endif
+
   documentaction *adocumentaction = new documentaction();
   adocumentaction->set_receptionist (&recpt);
Index: /main/trunk/greenstone2/runtime-src/src/recpt/pageaction.cpp
===================================================================
--- /main/trunk/greenstone2/runtime-src/src/recpt/pageaction.cpp	(revision 22045)
+++ /main/trunk/greenstone2/runtime-src/src/recpt/pageaction.cpp	(revision 22046)
@@ -725,5 +725,5 @@
     disp.setmacro("numbytes", displayclass::defaultpackage, numbytes);
 
-    // set up ct, qt, qto
+    // set up ct, qt, qto, sqlqto
     set_query_type_args(cinfo, args);
     // set up ks, ss, afs
Index: /main/trunk/greenstone2/runtime-src/src/recpt/queryaction.cpp
===================================================================
--- /main/trunk/greenstone2/runtime-src/src/recpt/queryaction.cpp	(revision 22045)
+++ /main/trunk/greenstone2/runtime-src/src/recpt/queryaction.cpp	(revision 22046)
@@ -38,21 +38,13 @@
 #include <assert.h>
 
-void colinfo_t::clear () {
-  formatlistptr = NULL;
-  browserptr = NULL;
-}
-
-void QueryResult_t::clear() {
-  doc.clear();
-  collection.clear();
-}
-
-queryaction::queryaction () {
-
-  recpt = NULL;
+
+queryaction::queryaction () 
+  : basequeryaction()
+{
   num_phrases = 0;
 
+  cgiarginfo arg_ainfo;
+
   // this action uses cgi variable "a"
-  cgiarginfo arg_ainfo;
   arg_ainfo.shortname = "a";
   arg_ainfo.longname = "action";
@@ -136,31 +128,4 @@
 
 
-  // "q"
-  arg_ainfo.shortname = "q";
-  arg_ainfo.longname = "query string";
-  arg_ainfo.multiplechar = true;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = g_EmptyText;
-  arg_ainfo.savedarginfo = cgiarginfo::must;
-  argsinfo.addarginfo (NULL, arg_ainfo);
-
-  // "q2"
-  arg_ainfo.shortname = "q2";
-  arg_ainfo.longname = "query string for second query";
-  arg_ainfo.multiplechar = true;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = g_EmptyText;
-  arg_ainfo.savedarginfo = cgiarginfo::must;
-  argsinfo.addarginfo (NULL, arg_ainfo);
-
-  // "cq2" ""=don't combine, "and", "or", "not"
-  arg_ainfo.shortname = "cq2";
-  arg_ainfo.longname = "combine queries";
-  arg_ainfo.multiplechar = true;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = g_EmptyText;
-  arg_ainfo.savedarginfo = cgiarginfo::must;
-  argsinfo.addarginfo (NULL, arg_ainfo);
-
   // "t" - 1 = ranked 0 = boolean
   arg_ainfo.shortname = "t";
@@ -226,30 +191,4 @@
   argsinfo.addarginfo (NULL, arg_ainfo);
  
-  // "m"
-  arg_ainfo.shortname = "m";
-  arg_ainfo.longname = "maximum number of documents";
-  arg_ainfo.multiplechar = true;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = "50";
-  arg_ainfo.savedarginfo = cgiarginfo::must;
-  argsinfo.addarginfo (NULL, arg_ainfo);
-
-  // "o"
-  arg_ainfo.shortname = "o";
-  arg_ainfo.longname = "hits per page";
-  arg_ainfo.multiplechar = true;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = "20";
-  arg_ainfo.savedarginfo = cgiarginfo::must;
-  argsinfo.addarginfo (NULL, arg_ainfo);
-
-  // "r"
-  arg_ainfo.shortname = "r";
-  arg_ainfo.longname = "start results from";
-  arg_ainfo.multiplechar = true;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = "1";
-  arg_ainfo.savedarginfo = cgiarginfo::must;
-  argsinfo.addarginfo (NULL, arg_ainfo);
 
   // "ccs"
@@ -281,26 +220,4 @@
   argsinfo.addarginfo (NULL, arg_ainfo);
 
-  // "hd" history display  - search history only displayed when
-  // this var set to something other than 0
-  // this number of records is displayed
-  arg_ainfo.shortname = "hd";
-  arg_ainfo.longname = "history display";
-  arg_ainfo.multiplechar = true;
-  arg_ainfo.multiplevalue = false;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = "0";
-  arg_ainfo.savedarginfo = cgiarginfo::must;
-  argsinfo.addarginfo (NULL, arg_ainfo);
-	    
-  // "hs"  save - set to 1 in query form, so only save when submit
-  // query
-  // 0 = no save 1 = save
-  arg_ainfo.shortname = "hs";
-  arg_ainfo.longname = "history save";
-  arg_ainfo.multiplechar = false;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = "0";
-  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
-  argsinfo.addarginfo (NULL, arg_ainfo);
 
   // "g" - new arg for granularity, for mgpp collections
@@ -376,33 +293,4 @@
   argsinfo.addarginfo (NULL, arg_ainfo);
 
-  // "fqn" - number of fields in the query form
-  arg_ainfo.shortname = "fqn";
-  arg_ainfo.longname = "form query num fields";
-  arg_ainfo.multiplechar = true;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = "4";
-  arg_ainfo.savedarginfo = cgiarginfo::must;
-  argsinfo.addarginfo (NULL, arg_ainfo);
-
-  // "fqf" - the list of field names in the form query
-  // - a comma separated list
-  arg_ainfo.shortname = "fqf";
-  arg_ainfo.longname = "form query fields";
-  arg_ainfo.multiplechar = true;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = g_EmptyText;
-  arg_ainfo.savedarginfo = cgiarginfo::must;
-  argsinfo.addarginfo (NULL, arg_ainfo);
-  
-  // "fqv" - the list of values in the form query
-  // - a comma separated list
-  arg_ainfo.shortname = "fqv";
-  arg_ainfo.longname = "form query values";
-  arg_ainfo.multiplechar = true;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = g_EmptyText;
-  arg_ainfo.savedarginfo = cgiarginfo::must;
-  argsinfo.addarginfo (NULL, arg_ainfo);
-  
 
   // "fqs" - the list of stemming options in the form query
@@ -427,63 +315,9 @@
   argsinfo.addarginfo (NULL, arg_ainfo);
   
-  // "fqc" - the list of boolean operators in the form query
-  // - a comma separated list
-  arg_ainfo.shortname = "fqc";
-  arg_ainfo.longname = "form query combines";
-  arg_ainfo.multiplechar = true;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = g_EmptyText;
-  arg_ainfo.savedarginfo = cgiarginfo::must;
-  argsinfo.addarginfo (NULL, arg_ainfo);
-
-  // "fqa" - form query advanced - for "run query"
-  arg_ainfo.shortname = "fqa";
-  arg_ainfo.longname = "form query advanced query";
-  arg_ainfo.multiplechar = false;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = "0";
-  arg_ainfo.savedarginfo = cgiarginfo::must;
-  argsinfo.addarginfo (NULL, arg_ainfo);
-
-  // "ifl" - I'm feeling lucky! (Go directly to a matching document)
-  arg_ainfo.shortname = "ifl";
-  arg_ainfo.longname = "i'm feeling lucky";
-  arg_ainfo.multiplechar = false;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = g_EmptyText;
-  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
-  argsinfo.addarginfo (NULL, arg_ainfo);
-
-  // "ifln" - I'm feeling lucky number (Go directly to the nth matching document)
-  arg_ainfo.shortname = "ifln";
-  arg_ainfo.longname = "i'm feeling lucky number";
-  arg_ainfo.multiplechar = true;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = "1";
-  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
-  argsinfo.addarginfo (NULL, arg_ainfo);
-
-  // "srn" - the next search result
-  arg_ainfo.shortname = "srn";
-  arg_ainfo.longname = "the next search result number";
-  arg_ainfo.multiplechar = true;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = "0";
-  arg_ainfo.savedarginfo = cgiarginfo::must;
-  argsinfo.addarginfo (NULL, arg_ainfo);
-
-  // "srp" - the previous search result
-  arg_ainfo.shortname = "srp";
-  arg_ainfo.longname = "the previous search result number";
-  arg_ainfo.multiplechar = true;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = "0";
-  arg_ainfo.savedarginfo = cgiarginfo::must;
-  argsinfo.addarginfo (NULL, arg_ainfo);
 
   // "sf" - Sort field. Set to field to be used for sorting search reult
   // set (only implemented for lucene collections at present).
-  arg_ainfo.shortname = "sf";
-  arg_ainfo.longname = "sort field";
+  arg_ainfo.shortname = "sqlsf";
+  arg_ainfo.longname = "sql sort field";
   arg_ainfo.multiplechar = true;
   arg_ainfo.defaultstatus = cgiarginfo::weak;
@@ -492,25 +326,21 @@
   argsinfo.addarginfo (NULL, arg_ainfo);
 
-  // "fuzziness" controls how closely the search terms must match
-  // 100 = exact match, 0 = very inexact match (only implemented for Lucene)
-  arg_ainfo.shortname = "fuzziness";
-  arg_ainfo.longname = "Lucene fuzziness value";
-  arg_ainfo.multiplechar = true;
-  arg_ainfo.defaultstatus = cgiarginfo::weak;
-  arg_ainfo.argdefault = g_EmptyText;
-  arg_ainfo.savedarginfo = cgiarginfo::must;
-  argsinfo.addarginfo (NULL, arg_ainfo);
+
+}
+
+queryaction::~queryaction () 
+{
 }
 
 void queryaction::configure (const text_t &key, const text_tarray &cfgline) {
-  action::configure (key, cfgline);
+  basequeryaction::configure (key, cfgline);
 }
 
 bool queryaction::init (ostream &logout) {
-  return action::init (logout);
+  return basequeryaction::init (logout);
 }
 
 bool queryaction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
-				 recptprotolistclass * /*protos*/, ostream &logout) {
+				 recptprotolistclass* protos, ostream &logout) {
 
   // check t argument
@@ -538,42 +368,4 @@
   }
 
-  // check m argument
-  int arg_m = args.getintarg("m");
-  if (arg_m < -1) {
-    logout << "Warning: \"m\" argument less than -1 (" << arg_m << ")\n";
-    cgiarginfo *minfo = argsinfo.getarginfo ("m");
-    if (minfo != NULL) args["m"] = minfo->argdefault;
-  }
-
-  // check o argument
-  int arg_o = args.getintarg("o");
-  if (arg_o < -1) {
-    logout << "Warning: \"o\" argument less than -1 (" << arg_o << ")\n";
-    cgiarginfo *oinfo = argsinfo.getarginfo ("o");
-    if (oinfo != NULL) args["o"] = oinfo->argdefault;
-  }
-
-  // check r argument
-  int arg_r = args.getintarg("r");
-  if (arg_r < 1) {
-    logout << "Warning: \"r\" argument less than 1 (" << arg_r << ")\n";
-    cgiarginfo *rinfo = argsinfo.getarginfo ("r");
-    if (rinfo != NULL) args["r"] = rinfo->argdefault;
-  }
-  //check hd argument
-  int arg_hd = args.getintarg("hd");
-  if (arg_hd <0 ) {
-    logout << "Warning: \"hd\" argument less than 0 (" << arg_hd << ")\n";
-    cgiarginfo *hdinfo = argsinfo.getarginfo ("hd");
-    if (hdinfo != NULL) args["hd"] = hdinfo->argdefault;
-  }  
-  
-  //check hs argument
-  int arg_hs = args.getintarg("hs");
-  if (arg_hs !=0 && arg_hs !=1) {
-    logout << "Warning: \"hs\" argument out of range (" << arg_hs << ")\n";
-    cgiarginfo *hsinfo = argsinfo.getarginfo ("hs");
-    if (hsinfo != NULL) args["hs"] = hsinfo->argdefault;
-  }
 
   // check ct argument
@@ -617,46 +409,15 @@
   }
 
-  return true;
-}
-
-void queryaction::get_cgihead_info (cgiargsclass &args, recptprotolistclass * /*protos*/,
-				    response_t &response, text_t &response_data, 
-				    ostream &/*logout*/) {
-  // If this is an "I'm feeling lucky" request, we don't know the target location until later
-  if (!args["ifl"].empty()) {
-    response = undecided_location;
-    return;
-  }
-
-  response = content;
-  response_data = "text/html";
+  return basequeryaction::check_cgiargs(argsinfo,args,protos,logout);
+
 }
 
 void queryaction::define_internal_macros (displayclass &disp, cgiargsclass &args, 
 					  recptprotolistclass * protos, 
-					  ostream &logout) {
-
-  // define_internal_macros sets the following macros:
-
-  // The following macros are set later (in define_query_macros) as they can't be set until 
-  // the query has been done.
-  // _quotedquery_   the part of the query string that was quoted for post-processing
-  // _freqmsg_      the term frequency string
-
-  // _resultline_   the "x documents matched the query" string
-
-  // _prevfirst_    these are used when setting up the links to previous/next
-  // _prevlast_     pages of results (_thisfirst_ and _thislast_ are used to set
-  // _nextfirst_    the 'results x-x for query: xxxx' string in the title bar)
-  // _nextlast_
-  // _thisfirst_
-  // _thislast_
-
-  
-  define_form_macros(disp, args, protos, logout);
-  
+					  ostream &logout) 
+{
+  basequeryaction::define_internal_macros(disp,args,protos,logout);
+
   define_query_interface(disp, args, protos, logout);
-
-
 }
 
@@ -693,43 +454,4 @@
 }
 
-
-// sets the selection box macros _hselection_, _jselection_, _nselection_ _gselection_, fqfselection_ 
-void queryaction::set_option_macro (const text_t &macroname, 
-				    text_t current_value, 
-				    bool display_single, 
-				    bool add_js_update,
-				    const FilterOption_t &option, 
-				    displayclass &disp) {
-  
-  if (option.validValues.empty()) return;
-  if (option.validValues.size() == 1) {
-    if (display_single) {
-      disp.setmacro (macroname + "selection", displayclass::defaultpackage, "_" + option.defaultValue + "_");
-    }
-    return;    
-  } 
-  if (option.validValues.size() < 2) return;
-  
-  text_t macrovalue = "<select name=\"" + macroname + "\"";
-  if (add_js_update) {
-    macrovalue += " onChange=\"update"+macroname+"();\"";
-  }
-  macrovalue += ">\n";
-  
-  if (current_value.empty()) current_value = option.defaultValue;
-  
-  text_tarray::const_iterator thisvalue = option.validValues.begin();
-  text_tarray::const_iterator endvalue = option.validValues.end();
-
-  while (thisvalue != endvalue) {
-    macrovalue += "<option value=\"" + *thisvalue + "\"";
-    if (*thisvalue == current_value)
-      macrovalue += " selected";
-    macrovalue += ">_" + *thisvalue + "_\n";
-    ++thisvalue;
-  }
-  macrovalue += "</select>\n";
-  disp.setmacro (macroname + "selection", displayclass::defaultpackage, macrovalue);
-}
 
 
@@ -832,4 +554,5 @@
 } // define external macros
   
+
 void queryaction::set_sfselection_macro(text_t current_value, 
 				       const FilterOption_t &option, 
@@ -924,6 +647,9 @@
   disp.setmacro ("gformselection", displayclass::defaultpackage, macrovalue);
 }
+
 void queryaction::define_form_macros (displayclass &disp, cgiargsclass &args, 
-			   recptprotolistclass *protos, ostream &logout) {
+				      recptprotolistclass *protos, 
+				      ostream &logout) 
+{
 
   // defines the following macros
@@ -952,72 +678,4 @@
   
 }
-
-void queryaction::define_history_macros (displayclass &disp, cgiargsclass &args, 
-					 recptprotolistclass *protos, ostream &logout) {
-
-  // defines the following macros
-  // _searchhistorylist_
-
-  text_t historylist;
-  int arghd = args.getintarg("hd");
-  if (arghd == 0) {
-    historylist="";
-  }
-  else {
-    historylist = "<!-- Search History List -->\n";
-   
-    text_t userid = args["z"];
-    text_tarray entries;
-    if (get_history_info (userid, entries, dbhome, logout)) {
-      int count = 1;
-      text_tarray::iterator here = entries.begin();
-      text_tarray::iterator end = entries.end();
-      int numrecords=(int)entries.size();
-      if (numrecords>arghd) { // only display some of them 
-	numrecords = arghd;
-      }
-      historylist += "<form action=\"_gwcgi_\" name=\"HistoryForm\"><table width=\"537\">\n";
-
-      for (int i=0; i<numrecords;++i) {
-	text_t query;
-	text_t numdocs;
-	text_t cgiargs;
-	text_t userinfo;
-	text_t escquery;
-	split_saved_query(entries[i],numdocs,cgiargs);
-	parse_saved_args(cgiargs, "q", query); // get query string out
-	decode_cgi_arg(query); // un cgisafe it
-	escquery = escape_quotes(query); // escape the quotes and newlines
-	text_t histvalue = "histvalue";
-	histvalue += i;
-	disp.setmacro(histvalue, "query", escquery);
-	format_user_info(cgiargs, userinfo, args, protos, logout);
-	
-	historylist += "<tr><td align=\"right\">_imagehistbutton_(";
-	historylist += i;
-	historylist += ")</td>\n";
-	historylist += "<td><table border=\"1\" cellspacing=\"0\" ";
-	historylist += "cellpadding=\"0\"><tr><td width=\"365\" align=\"left\">"
-	  + query 
-	  + "</td></tr></table></td><td width=\"110\" align=\"center\"><small>"
-	  + numdocs;
-	if (numdocs == 1) historylist += " _texthresult_";
-	else historylist += " _texthresults_";
-	if (!userinfo.empty()) {
-	  historylist += "<br>( "+userinfo+" )";
-	}
-	historylist += "</small></td>\n";
-      }
-      historylist+="</table></form>\n\n";
-     
-    } // if get history info 
-    else {
-      historylist += "_textnohistory_";
-    }
-    historylist += "<! ---- end of history list ----->\n";
-  } // else display list
-  disp.setmacro("searchhistorylist", "query", historylist);
-  
-} // define history macros
 
 void queryaction::output_ccp (cgiargsclass &args, recptprotolistclass *protos,
@@ -1153,6 +811,4 @@
   }
 
-
-
   if (args["ccs"] == "1") {
     if (!args["cc"].empty()) {
@@ -1177,4 +833,27 @@
   return true;
 }
+
+
+
+// request.filterResultOptions and request.fields (if required) should
+// be set from the calling code
+void queryaction::set_queryfilter_options (FilterRequest_t &request, 
+					   const text_t &querystring,
+					   cgiargsclass &args) 
+{
+  set_fulltext_queryfilter_options(request,querystring,args);
+}
+
+
+
+void queryaction::set_queryfilter_options (FilterRequest_t &request, 
+					   const text_t &querystring1,
+					   const text_t &querystring2, 
+					   cgiargsclass &args) 
+{
+  set_fulltext_queryfilter_options(request,querystring1,querystring2,args);
+}
+
+
 
 bool queryaction::search_multiple_collections (cgiargsclass &args, recptprotolistclass *protos,
@@ -1688,5 +1367,5 @@
   } // args["qt"]=1
   else {
-    logout << "ERROR (query_action::get_formatted_query_string): querytype not defined\n";
+    logout << "ERROR (queryaction::get_formatted_query_string): querytype not defined\n";
   }
 }
@@ -1700,52 +1379,13 @@
 // and search_multiple_coll
 void queryaction::define_query_macros (cgiargsclass &args, displayclass &disp,
-				       int numdocs, isapprox isApprox) {
-  
-  // set up _resultline_ macro
-  text_t resline;
-  int maxdocs = args.getintarg("m");
+				       int numdocs, isapprox isApprox) 
+{
+  // The following 'if' statatment is placed here to be keep the semantics 
+  // the same as the version before basequeryaction was introduced
+
   if (num_phrases > 0) isApprox = Exact;
-  if (maxdocs == -1) maxdocs = numdocs;
-  else if (numdocs > maxdocs) {
-    numdocs = maxdocs;
-    isApprox = MoreThan;
-  }
-
-  if (isApprox == Approximate) resline = "_textapprox_";
-  else if (isApprox == MoreThan) resline = "_textmorethan_";
-  
-  if (numdocs == 0) resline = "_textnodocs_";
-  else if (numdocs == 1) resline += "_text1doc_";
-  else resline += text_t(numdocs) + " _textlotsdocs_";
-  
-  disp.setmacro("resultline", "query", resline);
-
-  int firstdoc = args.getintarg("r");
-  int hitsperpage = args.getintarg("o");
-  if (hitsperpage == -1) hitsperpage = numdocs;
-
-  // set up _thisfirst_ and _thislast_ macros
-  disp.setmacro ("thisfirst", "query", firstdoc);
-  int thislast = firstdoc + (hitsperpage - 1);
-  if (thislast > numdocs) thislast = numdocs;
-  disp.setmacro ("thislast", "query", thislast);
-
-  // set up _prevfirst_ and _prevlast_ macros
-  if (firstdoc > 1) {
-    disp.setmacro ("prevlast", "query", firstdoc - 1);
-    int prevfirst = firstdoc - hitsperpage;
-    if (prevfirst < 1) prevfirst = 1;
-    disp.setmacro ("prevfirst", "query", prevfirst);
-  }
-
-  // set up _nextfirst_ and _nextlast_ macros
-  if (thislast < numdocs) {
-    disp.setmacro ("nextfirst", "query", thislast + 1);
-    int nextlast = thislast + hitsperpage;
-    if (nextlast > numdocs) nextlast = numdocs;
-    disp.setmacro ("nextlast", "query", nextlast);
-  }
-
-  // do quoted query here cos we may have added quotes during query pre-processing
+
+  basequeryaction::define_query_macros(args,disp,numdocs,isApprox);
+
   if (args["ct"]==0) { // mg queries only, not mgpp
     // get the quoted bits of the query string and set _quotedquery_
@@ -1772,39 +1412,4 @@
 }
 
-// define_single_query_macros sets the extra macros for search_single_coll 
-// that couldn't be set until the query had been done. Those macros are 
-// _freqmsg_ and _stopwordsmsg_
-void queryaction::define_single_query_macros (cgiargsclass &args, 
-					      displayclass &disp, 
-					      const FilterResponse_t &response) {
-  // set up _freqmsg_ and _stopwordsmsg_ macros
-
-  text_t freqmsg = "";
-  freqmsg = "_textfreqmsg1_";
-  TermInfo_tarray::const_iterator this_term = response.termInfo.begin();
-  TermInfo_tarray::const_iterator end_term = response.termInfo.end();
-  while (this_term != end_term) {
-    freqmsg += (*this_term).term + ": " + (*this_term).freq;
-    if ((this_term + 1) != end_term)
-      freqmsg += ", ";
-    ++this_term;
-  }
-  disp.setmacro ("freqmsg", "query", freqmsg);
-
-  text_tset::const_iterator this_stopword = response.stopwords.begin();
-  text_tset::const_iterator end_stopword = response.stopwords.end();
-  if (this_stopword != end_stopword) {
-    text_t stopwordsmsg = "_textstopwordsmsg_ ";
-    while (this_stopword != end_stopword) {
-      if (stopwordsmsg != "_textstopwordsmsg_ ") {
-	stopwordsmsg += ", ";
-      }
-      stopwordsmsg += (*this_stopword);
-      ++this_stopword;
-    }
-    disp.setmacro("stopwordsmsg", "query", stopwordsmsg);
-  }
-}
-
 // should this change for cross coll search??
 bool queryaction::save_search_history (cgiargsclass &args, int numdocs, 
@@ -1840,6 +1445,4 @@
   if (set_history_info(userid, query, dbhome, display)) return true;
   else return false;
-
-
-}
-
+}
+
Index: /main/trunk/greenstone2/runtime-src/src/recpt/queryaction.h
===================================================================
--- /main/trunk/greenstone2/runtime-src/src/recpt/queryaction.h	(revision 22045)
+++ /main/trunk/greenstone2/runtime-src/src/recpt/queryaction.h	(revision 22046)
@@ -29,38 +29,11 @@
 
 #include "gsdlconf.h"
-#include "action.h"
+#include "basequeryaction.h"
 #include "receptionist.h"
 
-struct colinfo_t {
-  void clear();
-  colinfo_t () {clear();}
 
-  format_t *formatlistptr;
-  browserclass *browserptr;
-};
-
-struct QueryResult_t {
-  void clear ();
-  QueryResult_t () {clear();}
-
-  ResultDocInfo_t doc;
-  text_t collection;
-};
-
-struct gteqQueryResult_t 
-{
-  bool operator()(const QueryResult_t &t1, const QueryResult_t &t2) const
-  { return t1.doc.ranking >= t2.doc.ranking; }
-};
-
-typedef set<QueryResult_t, gteqQueryResult_t> QueryResult_tset;
-
-
-class queryaction : public action {
+class queryaction : public basequeryaction {
 
 protected:
-
-  receptionist *recpt;
-
   text_t m_strUseInterfaceLanguageForQuery;
   text_t formatstring;
@@ -68,25 +41,21 @@
   int num_phrases;
 
-  virtual void define_single_query_macros (cgiargsclass &args, 
+  virtual text_t query_filter_name () {return "QueryFilter";}
+
+  virtual void define_query_macros (cgiargsclass &args, displayclass &disp, 
+				    int numDocs, isapprox isApprox); 
+
+  virtual void get_formatted_query_string (text_t &formattedstring, 
+					   bool segment, 
+					   cgiargsclass &args, 
 					   displayclass &disp, 
-					   const FilterResponse_t &response); 
-  virtual void define_query_macros (cgiargsclass &args, displayclass &disp, 
-			    int numDocs, isapprox isApprox); 
+					   ostream &logout);
 
-  void get_formatted_query_string (text_t &formattedstring, bool segment, 
-				   cgiargsclass &args, 
-				   displayclass &disp, ostream &logout);
   void define_query_interface(displayclass &disp, cgiargsclass &args, 
 			      recptprotolistclass *protos, ostream &logout);
   
-  void define_form_macros (displayclass &disp, cgiargsclass &args, 
-			   recptprotolistclass *protos, ostream &logout);
-
-  void define_history_macros (displayclass &disp, cgiargsclass &args, 
-			       recptprotolistclass *protos, ostream &logout);
-
-  virtual void set_option_macro (const text_t &macroname, text_t current_value,
-				 bool display_single, bool add_js_update,
-			 const FilterOption_t &option, displayclass &disp);
+  virtual void define_form_macros (displayclass &disp, cgiargsclass &args, 
+				   recptprotolistclass *protos, 
+				   ostream &logout);
 
   void set_gformselection_macro (text_t current_value,
@@ -100,4 +69,15 @@
 		   displayclass &disp, outconvertclass &outconvert,
 		   ostream &textout, ostream &logout);
+
+  virtual void set_queryfilter_options (FilterRequest_t &request, 
+					const text_t &querystring,
+					cgiargsclass &args);
+
+  virtual void set_queryfilter_options (FilterRequest_t &request, 
+					const text_t &querystring1,
+					const text_t &querystring2, 
+					cgiargsclass &args);
+
+
   
   bool search_multiple_collections (cgiargsclass &args, 
@@ -118,12 +98,11 @@
 
 
-  bool save_search_history(cgiargsclass &args, int numdocs, isapprox isApprox);
+  virtual bool save_search_history(cgiargsclass &args, int numdocs, 
+				   isapprox isApprox);
 
 public:
   queryaction ();
-  virtual ~queryaction () {}
+  virtual ~queryaction ();
 
-  void set_receptionist (receptionist *therecpt) {recpt=therecpt;}
-  
   void configure (const text_t &key, const text_tarray &cfgline);
   bool init (ostream &logout);
@@ -134,8 +113,4 @@
 			      recptprotolistclass *protos, ostream &logout);
 
-  void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
-			 response_t &response, text_t &response_data, 
-			 ostream &logout);
-  
   virtual void define_internal_macros (displayclass &disp, cgiargsclass &args, 
 			       recptprotolistclass *protos, ostream &logout);
Index: /main/trunk/greenstone2/runtime-src/src/recpt/querytools.cpp
===================================================================
--- /main/trunk/greenstone2/runtime-src/src/recpt/querytools.cpp	(revision 22045)
+++ /main/trunk/greenstone2/runtime-src/src/recpt/querytools.cpp	(revision 22046)
@@ -82,5 +82,5 @@
     args.setintarg("qto", type);
   } 
-  
+
   if (args["qt"].empty()) {
     int arg_qto = args.getintarg("qto");
@@ -91,4 +91,23 @@
     }
   }
+
+
+  // decide if sqlqto should be set or not
+  unsigned int sql_type = 0;
+  text_t infodb_type = cinfo->infodbType;
+  if ((infodb_type == "sqlite") || (infodb_type == "mssql")) {
+    if (findword(search_types.begin(), search_types.end(), "sqlform") != search_types.end()) {
+      sql_type = 1;
+    }
+  }
+
+  if (sql_type) {
+    args["sqlqto"] = "1";
+  }
+  else {
+    args["sqlqto"] = "0";
+  }
+
+
 }
 
@@ -109,9 +128,38 @@
 }
 
+
+
+void set_basequeryfilter_options (FilterRequest_t &request, 
+				  cgiargsclass &args) 
+{
+
+  OptionValue_t option;
+  int arg_m = args.getintarg("m");
+  
+  option.name = "Maxdocs";
+  option.value = arg_m;
+  request.filterOptions.push_back (option);
+
+  //  option.name = "StartResults";
+  //  option.value = args["r"];
+  //  request.filterOptions.push_back (option);
+
+  //  option.name = "EndResults";
+  //  int endresults = args.getintarg("o") + (args.getintarg("r") - 1);
+  //  if ((endresults > arg_m) && (arg_m != -1)) endresults = arg_m;
+  //  option.value = endresults;
+  //  request.filterOptions.push_back (option);
+}
+
+
 // request.filterResultOptions and request.fields (if required) should
 // be set from the calling code
-void set_queryfilter_options (FilterRequest_t &request, 
-			      const text_t &querystring,
-			      cgiargsclass &args) {
+void set_fulltext_queryfilter_options (FilterRequest_t &request, 
+				       const text_t &querystring,
+				       cgiargsclass &args) 
+{
+  // better if this function, and the two-query companion function
+  // was implemented in queryaction.cpp
+  // Has to be done here to documentaction.cpp can call it directly
 
   request.filterName = "QueryFilter";
@@ -190,12 +238,16 @@
   }
 
-  set_more_queryfilter_options (request, args);
-}
-
-void set_queryfilter_options (FilterRequest_t &request, 
-			      const text_t &querystring1,
-			      const text_t &querystring2, cgiargsclass &args) {
-
-  set_queryfilter_options (request, querystring1, args);
+  set_basequeryfilter_options(request, args);
+}
+
+
+
+void set_fulltext_queryfilter_options (FilterRequest_t &request, 
+				       const text_t &querystring1,
+				       const text_t &querystring2, 
+				       cgiargsclass &args) 
+{
+
+  set_fulltext_queryfilter_options (request, querystring1, args);
 
   // fill in the second query if needed
@@ -245,27 +297,29 @@
     }
   }
-  set_more_queryfilter_options (request, args);
-}
-
-void set_more_queryfilter_options (FilterRequest_t &request, 
-				   cgiargsclass &args) {
-
-  OptionValue_t option;
-  int arg_m = args.getintarg("m");
-  
-  option.name = "Maxdocs";
-  option.value = arg_m;
-  request.filterOptions.push_back (option);
-
-  //  option.name = "StartResults";
-  //  option.value = args["r"];
-  //  request.filterOptions.push_back (option);
-
-  //  option.name = "EndResults";
-  //  int endresults = args.getintarg("o") + (args.getintarg("r") - 1);
-  //  if ((endresults > arg_m) && (arg_m != -1)) endresults = arg_m;
-  //  option.value = endresults;
-  //  request.filterOptions.push_back (option);
-}
+
+  // this is probably redundant, as first line to this method will have
+  // already caused it to invoke set_basequeryfilter_options
+
+  set_basequeryfilter_options(request, args);
+}
+
+
+
+// request.filterResultOptions and request.fields (if required) should
+// be set from the calling code
+void set_sql_queryfilter_options (FilterRequest_t &request, 
+				  cgiargsclass &args) 
+{
+  if (!args["sqlsf"].empty()) { // sort field for lucene
+    OptionValue_t option;
+
+    option.name = "SortField";
+    option.value = args["sqlsf"];
+    request.filterOptions.push_back (option);
+  }
+
+  set_basequeryfilter_options(request, args);
+}
+
 
 bool is_special_character(int indexer_type, unsigned short character) {
@@ -551,8 +605,11 @@
     if (!values[i].empty()) {
       text_t this_value = values[i];
+
       // remove operators for simple search, segments text if necessary
       format_querystring(this_value, argb, segment); 
+           
       // add tag info for this field (and other processing)
       format_field_info(this_value, fields[i], argct, argt, argb);
+
       // add into query string
       if (argct == 2) {
@@ -644,4 +701,148 @@
   }
 }
+
+
+// SQL versions for parsing query form
+
+void parse_sqlreg_query_form(text_t &querystring, cgiargsclass &args, bool segment)
+{
+  querystring.clear();
+
+  int argt = args.getintarg("t");// t=0 -and, t=1 - or
+  int argb = args.getintarg("b");
+    
+  text_t combine;
+
+  if (argt == 0) combine = "AND";
+  else combine = "OR";
+  
+  text_t field = args["sqlfqf"];
+  if (field.empty()) return; // no query
+  text_tarray fields;
+  splitchar(field.begin(), field.end(), ',', fields); 
+
+  text_t sqlcomb = args["sqlfqc"];
+  if (sqlcomb.empty()) return; //somethings wrong
+  text_tarray sqlcombs;
+  splitchar(sqlcomb.begin(), sqlcomb.end(), ',', sqlcombs);
+  
+  text_t value = args["fqv"];
+  if (value.empty()) return; // somethings wrong
+  text_tarray values;
+  splitchar(value.begin(), value.end(), ',', values);
+
+
+  for (int i=0; i< values.size(); ++i) {
+    if (!values[i].empty()) {
+      text_t this_value = values[i];
+
+      // remove operators for simple search, segments text if necessary
+      format_querystring(this_value, argb, segment); 
+           
+      // add tag info for this field (and other processing)
+      format_field_info_sql(this_value, fields[i], sqlcombs[i], argt, argb);
+
+      const text_t DISTINCT_SELECT_WHERE
+	= "SELECT DISTINCT docOID FROM document_metadata WHERE ";
+
+      if (querystring.empty()) {
+	// first query term
+	querystring = DISTINCT_SELECT_WHERE + this_value;
+      }
+      else {
+	this_value = DISTINCT_SELECT_WHERE + this_value;
+
+	if (combine=="AND") {	
+	  // INNER JOIN to restrict to only matching docOIDs
+	  querystring = "SELECT docOID FROM (" + querystring + ")"
+	    + " INNER JOIN (" + this_value +") USING (docOID)";
+	}
+	else if (combine=="OR") {
+	  // Union to allow union of the two
+	  querystring = querystring + " UNION " + this_value;
+	}
+      }
+    }
+  }
+}
+
+
+void parse_sqladv_query_form(text_t &querystring, cgiargsclass &args, 
+			     bool segment)
+{
+  querystring.clear();
+
+  int argt = 0; // set it to 0 = AND, by default
+  int argb = args.getintarg("b");
+  text_t combine = "AND";
+
+  text_t field = args["sqlfqf"];
+
+  if (field.empty()) return; // no query
+  text_tarray fields;
+  splitchar(field.begin(), field.end(), ',', fields); 
+  
+  text_t sqlcomb = args["sqlfqc"];
+  if (sqlcomb.empty()) return; //somethings wrong
+  text_tarray sqlcombs;
+  splitchar(sqlcomb.begin(), sqlcomb.end(), ',', sqlcombs);
+
+  text_t value = args["fqv"];
+  if (value.empty()) return; // somethings wrong
+  text_tarray values;
+  splitchar(value.begin(), value.end(), ',', values);
+
+  text_t comb = args["fqc"];
+  if (comb.empty()) return; //somethings wrong
+  text_tarray combs;
+  splitchar(comb.begin(), comb.end(), ',', combs);
+
+  for(int i=0; i< values.size(); ++i) {
+    if (!values[i].empty()) {
+      if (i>0) {
+	if (combs[i-1]=="and") { combine = "AND"; }
+	else if (combs[i-1]=="or") { combine = "OR"; }
+	else if (combs[i-1]=="not") { combine = "NOT"; }
+      }
+      text_t this_value = values[i];
+
+      // remove operators for simple search, segments text if necessary
+      format_querystring(this_value, argb, segment); 
+
+      // add tag info for this field (and other processing)
+      format_field_info_sql(this_value, fields[i], sqlcombs[i], argt, argb);
+
+      // add into query string
+
+      const text_t DISTINCT_SELECT_WHERE
+	= "SELECT DISTINCT docOID FROM document_metadata WHERE ";
+
+      if (querystring.empty()) {
+	// first query term
+	querystring = DISTINCT_SELECT_WHERE + this_value;
+      }
+      else {
+	this_value = DISTINCT_SELECT_WHERE + this_value;
+
+	if (combine=="AND") {	
+	  // INNER JOIN to restrict to only matching docOIDs
+	  querystring = "SELECT docOID FROM (" + querystring + ")"
+	    + " INNER JOIN (" + this_value +") USING (docOID)";
+	}
+	else if (combine=="OR") {
+	  // Union to allow union of the two
+	  querystring = querystring + " UNION " + this_value;
+	}
+	else {
+	  cerr << "Unsupported combination operation: " << combine << endl;
+	}
+      }
+      
+    }
+  }  
+}
+
+
+
 
 // Extended addqueryelem for Human Info project
@@ -738,4 +939,95 @@
     querystring = tag+":("+querystring+")";
   }
+    
+}
+
+
+void add_field_info_sql(text_t &querystring, const text_t &tagseq, 
+			const text_t& sqlcomb) 
+{
+
+  if (tagseq == "") return; // do nothing
+
+  text_t element_in = "(element IN (";
+
+  text_tlist mdterms;
+
+  splitword(tagseq.begin(), tagseq.end(), "/", mdterms);
+
+  text_t tags_in = "";
+
+  while (!mdterms.empty()) {
+    text_t tag = mdterms.front();
+    mdterms.pop_front();
+
+    if (!tag.empty()) {
+
+      if (tag.size()>3 && (substr(tag.begin(), tag.begin()+3) == "ex.")) {
+	tag = substr (tag.begin()+3, tag.end());
+      }
+
+      if (!tags_in.empty()) {
+	tags_in += ",";
+      }
+      
+      tags_in += "'" + tag + "'";
+    }
+  }
+
+  element_in += tags_in + ") AND (";
+
+  if (sqlcomb == "=") {
+    // override what it means to do equality, to make it more like full text
+    // searching
+
+    text_t orterms = "";
+    text_t term = "";
+    bool in_phrase = false;
+    
+    text_t::const_iterator here = querystring.begin();
+    text_t::const_iterator end = querystring.end();
+    while (here != end) {
+      if (is_unicode_letdig(*here)) {
+	term.push_back(*here);
+      }
+      else if (*here == '"') {
+	term.push_back(*here);
+	if (!in_phrase) {
+	  in_phrase = true;
+	} else {
+	  in_phrase = false;
+	}
+      }      
+      else if (in_phrase) {
+        // Found word boundary, but in a phrase, so does not complete term
+	term.push_back(*here);
+      }
+      else {
+        // Found a word boundary
+	if (!orterms.empty()) {
+	  orterms += " OR ";
+	}
+	orterms += "value LIKE '%" + term + "%'";
+	term.clear();
+      }
+      ++here;
+    }
+
+    if (!term.empty()) {
+	if (!orterms.empty()) {
+	  orterms += " OR ";
+	}
+      	orterms += "value LIKE '%" + term + "%'";
+    }
+
+    element_in += orterms;
+  }
+  else {
+    // search on value is "as is" querystring
+    element_in += "value " + sqlcomb + " '" + querystring+"'";
+  }
+
+  
+  querystring = element_in + "))";
     
 }
@@ -901,6 +1193,14 @@
     finalquery.push_back(' '); 
   }
-
+ 
   querystring  = finalquery;
+}
+
+
+void format_field_info_sql(text_t &querystring, const text_t &tagseq, 
+			   const text_t &sqlcomb, 
+			   int argt, int argb) 
+{
+  add_field_info_sql(querystring, tagseq, sqlcomb);
 }
 
Index: /main/trunk/greenstone2/runtime-src/src/recpt/querytools.h
===================================================================
--- /main/trunk/greenstone2/runtime-src/src/recpt/querytools.h	(revision 22045)
+++ /main/trunk/greenstone2/runtime-src/src/recpt/querytools.h	(revision 22046)
@@ -34,9 +34,19 @@
 void set_stem_index_args(ColInfoResponse_t *cinfo, cgiargsclass &args);
 
-void set_queryfilter_options (FilterRequest_t &request, const text_t &querystring,
-			      cgiargsclass &args);
+void set_basequeryfilter_options (FilterRequest_t &request, 
+				  cgiargsclass &args);
 
-void set_queryfilter_options (FilterRequest_t &request, const text_t &querystring1,
-			      const text_t &querystring2, cgiargsclass &args);
+void set_fulltext_queryfilter_options (FilterRequest_t &request, 
+				       const text_t &querystring,
+				       cgiargsclass &args);
+
+void set_fulltext_queryfilter_options (FilterRequest_t &request, 
+				       const text_t &querystring1,
+				       const text_t &querystring2, 
+				       cgiargsclass &args);
+
+void set_sql_queryfilter_options (FilterRequest_t &request, 
+				  cgiargsclass &args);
+
 
 // don't hassle the function name ... it's been a long day ;)
@@ -58,4 +68,8 @@
 void parse_adv_query_form(text_t &formattedstring, cgiargsclass &args, bool segment);
 
+// similar, but for SQL
+void parse_sqlreg_query_form(text_t &formattedstring, cgiargsclass &args, bool segment);
+void parse_sqladv_query_form(text_t &formattedstring, cgiargsclass &args, bool segment);
+
 // Added for Human Info project
 void addqueryelem_ex(text_t &querystring, const text_t &tag, 
@@ -64,4 +78,7 @@
 		     const text_t& combine, const text_t& word_combine);
 
+void format_field_info_sql(text_t &querystring, const text_t& tagseq, 
+			   const text_t& sqlcomb, int argt, int argb);
+
 void format_field_info(text_t &querystring, text_t tag, int argct, int argt, int argb);
 
