/*++++++++++++++++++++
  dbfncs.c: refdb database-specific support functions
  markus@mhoenicka.de 2002-08-06

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.
   
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
   
  You should have received a copy of the GNU General Public License
  along with this program; if not, see <http://www.gnu.org/licenses/>

  +++++++++++++++++++++++++*/

#include <dbi/dbi.h>
#include <syslog.h>
#include <string.h>
#include <stdio.h>

#include "linklist.h"
#include "refdb.h"
#include "refdbd.h"
#include "strfncs.h"
#include "dbfncs.h"
#include "connect.h"

/* global variables */
extern int n_log_level;
extern char main_db[];
extern struct db_caps* ptr_dbcaps;

/* forward declarations of static functions */
static int my_dbi_conn_transaction(dbi_conn conn, int trans_type);
static int set_cap_versioninfo(dbi_conn conn, const char* drivername);

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  new_db_caps(): creates and initializes a new db_caps struct

  struct db_caps* new_db_caps returns a ptr to the struct if successful
  NULL if out of memory

  struct CLIENT_REQUEST* ptr_clrequest ptr to structure with client info

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
struct db_caps* new_db_caps(struct CLIENT_REQUEST* ptr_clrequest) {
  struct db_caps* ptr_caps;

  if ((ptr_caps = malloc(sizeof(struct db_caps))) == NULL) {
    return NULL;
  }

  strcpy(ptr_caps->defval, "f");

  if (!strcmp(ptr_clrequest->dbserver, "mysql")) {
    /* these settings should work for 3.23.19 and later. We'll check the
       database engine version at runtime and fill in more appropriate
       values if we're using a newer version. Some settings (like
       transactions) are not supported by older versions, but do not cause
       an error */
    strcpy(ptr_caps->multiple_db, "t");
    strcpy(ptr_caps->sql_enum, "t");
    strcpy(ptr_caps->rlike, "RLIKE BINARY");
    strcpy(ptr_caps->not_rlike, "NOT RLIKE BINARY");
    strcpy(ptr_caps->transaction, "t");
    strcpy(ptr_caps->localhost, "localhost");
    strcpy(ptr_caps->encoding, "");
    strcpy(ptr_caps->groups, "f");
    strcpy(ptr_caps->admin_systable, "mysql.user");
    strcpy(ptr_caps->listall, ".*");
    strcpy(ptr_caps->bigint, "t");
    strcpy(ptr_caps->sql_union, "f");
    strcpy(ptr_caps->named_seq, "f");
    strcpy(ptr_caps->charlength, "CHAR_LENGTH");
    strcpy(ptr_caps->substring, "SUBSTRING");
    strcpy(ptr_caps->substring_from, " FROM ");
    strcpy(ptr_caps->substring_for, " FOR ");
    strcpy(ptr_caps->sql_except, "f");
    strcpy(ptr_caps->unix_regexp, "t");
    ptr_caps->has_versioninfo = 0;
  }
  else if (!strcmp(ptr_clrequest->dbserver, "pgsql")) {
    strcpy(ptr_caps->multiple_db, "f");
    strcpy(ptr_caps->sql_enum, "f");
    strcpy(ptr_caps->rlike, "~");
    strcpy(ptr_caps->not_rlike, "!~");
    strcpy(ptr_caps->transaction, "t");
    strcpy(ptr_caps->localhost, "");
    strcpy(ptr_caps->encoding, "WITH ENCODING =");
    strcpy(ptr_caps->groups, "t");
    strcpy(ptr_caps->admin_systable, "pg_shadow");
    strcpy(ptr_caps->listall, ".*");
    strcpy(ptr_caps->bigint, "t");
    strcpy(ptr_caps->sql_union, "t");
    strcpy(ptr_caps->named_seq, "t");
    strcpy(ptr_caps->charlength, "CHAR_LENGTH");
    strcpy(ptr_caps->substring, "SUBSTRING");
    strcpy(ptr_caps->substring_from, " FROM ");
    strcpy(ptr_caps->substring_for, " FOR ");
    strcpy(ptr_caps->sql_except, "t");
    strcpy(ptr_caps->unix_regexp, "t");
    ptr_caps->has_versioninfo = 1;
  }
  else if (!strcmp(ptr_clrequest->dbserver, "sqlite")) {
    strcpy(ptr_caps->multiple_db, "f");
    strcpy(ptr_caps->sql_enum, "f");
    strcpy(ptr_caps->rlike, "LIKE");
    strcpy(ptr_caps->not_rlike, "NOT LIKE");
    strcpy(ptr_caps->transaction, "t");
    strcpy(ptr_caps->localhost, "NA");
    strcpy(ptr_caps->encoding, "");
    strcpy(ptr_caps->groups, "NA");
    strcpy(ptr_caps->admin_systable, "NA");
    strcpy(ptr_caps->listall, "%");
    strcpy(ptr_caps->bigint, "f");
    strcpy(ptr_caps->sql_union, "t");
    strcpy(ptr_caps->named_seq, "f");
    strcpy(ptr_caps->charlength, "LENGTH");
    strcpy(ptr_caps->substring, "SUBSTR");
    strcpy(ptr_caps->substring_from, ",");
    strcpy(ptr_caps->substring_for, ",");
    strcpy(ptr_caps->sql_except, "t");
    strcpy(ptr_caps->unix_regexp, "f");
    ptr_caps->has_versioninfo = 1;
  }
  else if (!strcmp(ptr_clrequest->dbserver, "sqlite3")) {
    strcpy(ptr_caps->multiple_db, "f");
    strcpy(ptr_caps->sql_enum, "f");
    strcpy(ptr_caps->rlike, "LIKE");
    strcpy(ptr_caps->not_rlike, "NOT LIKE");
    strcpy(ptr_caps->transaction, "t");
    strcpy(ptr_caps->localhost, "NA");
    strcpy(ptr_caps->encoding, "");
    strcpy(ptr_caps->groups, "NA");
    strcpy(ptr_caps->admin_systable, "NA");
    strcpy(ptr_caps->listall, "%");
    strcpy(ptr_caps->bigint, "t");
    strcpy(ptr_caps->sql_union, "t");
    strcpy(ptr_caps->named_seq, "f");
    strcpy(ptr_caps->charlength, "LENGTH");
    strcpy(ptr_caps->substring, "SUBSTR");
    strcpy(ptr_caps->substring_from, ",");
    strcpy(ptr_caps->substring_for, ",");
    strcpy(ptr_caps->sql_except, "t");
    strcpy(ptr_caps->unix_regexp, "f");
    ptr_caps->has_versioninfo = 1;
  }
  else {
    LOG_PRINT(LOG_WARNING, "unknown libdbi driver requested");
    free(ptr_caps);
    return NULL;
  }

  return ptr_caps;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  free_db_caps(): frees the memory of a db_caps struct

  free_db_caps returns nothing

  struct db_caps* ptr_caps ptr to a db_caps structure

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void free_db_caps(struct db_caps* ptr_caps) {
  if (ptr_caps) {
    free(ptr_caps);
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  create_tables_mysql(): creates MySQL-specific reference data tables

  int create_tables_mysql returns >0 if error, 0 if successful 

  dbi_conn conn database connection structure

  struct CLIENT_REQUEST* ptr_clrequest ptr to structure with client info

  int is_temp if nonzero, create a temporary table, otherwise a permanent table

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int create_tables_mysql(dbi_conn conn, struct CLIENT_REQUEST* ptr_clrequest, int is_temp) {
  char* sql_command;
  char temporary[] = TEMP_TABLE_SPECIFIER;
  char prefix[] = TEMP_TABLE_NAME_PREFIX;
  size_t sql_command_len = 2048;
  dbi_result dbires;

  /* get us some memory for the query strings */
  if ((sql_command = malloc(sql_command_len)) == NULL) {
    return 801;
  }

  /* fix the inserts */
  if (!is_temp) {
    *temporary = '\0';
    *prefix = '\0';
  }

  /* create the metadata table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%smeta ("
	   "meta_app VARCHAR(20),"
	   "meta_type VARCHAR(20),"
	   "meta_version VARCHAR(20),"
	   "meta_dbversion SMALLINT,"
	   "meta_create_date DATETIME,"
	   "meta_modify_date DATETIME)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(211));
    return 211;
  }
   
  dbi_result_free(dbires);

  /* create the main table */
  /* Version issue: later versions support something like:
     refdb_id INT NOT NULL PRIMARY KEY AUTO_INCREMENT 
     without a separate PRIMARY KEY statement */
  /* using a DATE field for refdb_pubyear is not possible as MySQL < 3.23
     does not allow to store dates like 2000-00-00, i.e. when only the
     year is known */
  /* refdb_periodical_id is actually a FOREIGN KEY, but MySQL does not
     support this so a simple INT has to do the trick */

  /* todo: the next incarnation of the internal representation of
     reference data might use properties, i.e. key-value pairs in a
     separate table, instead of a wealth of (mostly empty)
     special-purpose columns */

  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%srefdb ("
	   "refdb_id BIGINT NOT NULL AUTO_INCREMENT,"
	   "refdb_citekey VARCHAR(255),"
	   "refdb_type VARCHAR(6),"
	   "refdb_pubyear SMALLINT NOT NULL,"
	   "refdb_secyear SMALLINT NOT NULL,"
	   "refdb_startpage VARCHAR(255),"
	   "refdb_endpage VARCHAR(255),"
	   "refdb_abstract MEDIUMTEXT,"
	   "refdb_title TEXT,"                  /* part title */
	   "refdb_volume VARCHAR(255),"
	   "refdb_issue VARCHAR(255),"
	   "refdb_booktitle TEXT,"              /* publication title */
	   "refdb_city VARCHAR(255),"
	   "refdb_publisher VARCHAR(255),"
	   "refdb_title_series TEXT,"           /* set title */
	   "refdb_address TEXT,"
	   "refdb_issn VARCHAR(255),"
	   "refdb_pyother_info VARCHAR(255),"
	   "refdb_secother_info VARCHAR(255),"
	   "refdb_periodical_id BIGINT,"
	   "refdb_user1 VARCHAR(255),"
	   "refdb_user2 VARCHAR(255),"
	   "refdb_user3 VARCHAR(255),"
	   "refdb_user4 VARCHAR(255),"
	   "refdb_user5 VARCHAR(255),"
	   "refdb_typeofwork VARCHAR(255),"
	   "refdb_area VARCHAR(255),"
	   "refdb_ostype VARCHAR(255),"
	   "refdb_degree VARCHAR(255),"
	   "refdb_runningtime VARCHAR(255),"
	   "refdb_classcodeintl VARCHAR(255),"
	   "refdb_classcodeus VARCHAR(255),"
	   "refdb_senderemail VARCHAR(255),"
	   "refdb_recipientemail VARCHAR(255),"
	   "refdb_mediatype VARCHAR(255),"
	   "refdb_numvolumes VARCHAR(255),"
	   "refdb_edition VARCHAR(255),"
	   "refdb_computer VARCHAR(255),"
	   "refdb_conferencelocation VARCHAR(255),"
	   "refdb_registrynum VARCHAR(255),"
	   "refdb_classification VARCHAR(255),"
	   "refdb_section VARCHAR(255),"
	   "refdb_pamphletnum VARCHAR(255),"
	   "refdb_chapternum VARCHAR(255),"
	   "KEY (refdb_pubyear),"
	   "UNIQUE (refdb_citekey),"
	   "PRIMARY KEY (refdb_id))", temporary, prefix);

  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(212));
    return 212;
  }
   
  dbi_result_free(dbires);

  /* create the author table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sauthor ("
	   "author_id BIGINT NOT NULL AUTO_INCREMENT,"
	   "author_name VARCHAR(255) NOT NULL,"
	   "author_lastname VARCHAR(255),"
	   "author_firstname VARCHAR(255),"
	   "author_middlename VARCHAR(255),"
	   "author_suffix VARCHAR(255),"
	   "PRIMARY KEY (author_id),"
	   "KEY (author_name(7)))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(213));
    return 213;
  }
   
  dbi_result_free(dbires);

  /* create the keyword table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%skeyword ("
	   "keyword_id BIGINT NOT NULL AUTO_INCREMENT,"
	   "keyword_name VARCHAR(255) NOT NULL,"
	   "PRIMARY KEY (keyword_id),"
	   "KEY (keyword_name(10)))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(214));
    return 214;
  }
   
  dbi_result_free(dbires);

  /* create the periodical table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%speriodical ("
	   "periodical_id BIGINT NOT NULL AUTO_INCREMENT,"
	   "periodical_name VARCHAR(255) NOT NULL,"
	   "periodical_abbrev VARCHAR(255) NOT NULL,"
	   "periodical_custabbrev1 VARCHAR(255) NOT NULL,"
	   "periodical_custabbrev2 VARCHAR(255) NOT NULL,"
	   "PRIMARY KEY (periodical_id),"
	   "KEY (periodical_name(20)),"
	   "KEY (periodical_abbrev(5)),"
	   "KEY (periodical_custabbrev1(5)),"
	   "KEY (periodical_custabbrev2(5)))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(215));
    return 215;
  }
   
  dbi_result_free(dbires);

  /* create the notes table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%snote ("
	   "note_id BIGINT NOT NULL AUTO_INCREMENT,"
	   "note_key VARCHAR(255),"
	   "note_title VARCHAR(255),"
	   "note_content TEXT,"
	   "note_content_type VARCHAR(255),"
	   "note_content_xmllang VARCHAR(255),"
	   "note_user_id BIGINT,"
	   "note_date DATE,"
	   "note_share SMALLINT,"
	   "KEY (note_title),"
	   "KEY (note_user_id),"
	   "KEY (note_date),"
	   "UNIQUE (note_key),"
	   "PRIMARY KEY (note_id))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(216));
    return 216;
  }
   
  dbi_result_free(dbires);

  /* create the user table */
  /* the user_name length limit is imposed by MySQL, don't know about PostgreSQL */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%suser ("
	   "user_id BIGINT NOT NULL AUTO_INCREMENT,"
	   "user_name VARCHAR(16) NOT NULL,"
	   "PRIMARY KEY (user_id),"
	   "KEY (user_name(8)))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(217));
    return 217;
  }
   
  dbi_result_free(dbires);

  /* create the links table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%slink ("
	   "link_id BIGINT NOT NULL AUTO_INCREMENT,"
	   "link_url TEXT NOT NULL,"
	   "PRIMARY KEY (link_id),"
	   "KEY (link_url(16)))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(257));
    return 257;
  }
   
  dbi_result_free(dbires);

  /* create the author xtable */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxauthor ("
	   "xauthor_id BIGINT NOT NULL AUTO_INCREMENT,"
	   "author_id BIGINT NOT NULL,"
	   "refdb_id BIGINT NOT NULL,"
	   "xauthor_type ENUM('part', 'publication', 'set'),"
	   "xauthor_role VARCHAR(64),"
	   "xauthor_position INT,"
	   "PRIMARY KEY (xauthor_id),"
	   "KEY (author_id),"
	   "KEY (refdb_id))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(218));
    return 218;
  }
   
  dbi_result_free(dbires);

  /* create the keyword xtable */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxkeyword ("
	   "xkeyword_id BIGINT NOT NULL AUTO_INCREMENT,"
	   "keyword_id BIGINT NOT NULL,"
	   "xref_id BIGINT NOT NULL,"
	   "xkeyword_type ENUM(\"REFERENCE\",\"NOTE\"),"
	   "PRIMARY KEY (xkeyword_id),"
	   "KEY (keyword_id),"
	   "KEY (xref_id))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(219));
    return 219;
  }
   
  dbi_result_free(dbires);

  /* create the user xtable */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxuser ("
	   "xuser_id BIGINT NOT NULL AUTO_INCREMENT,"
	   "user_id BIGINT NOT NULL,"
	   "refdb_id BIGINT NOT NULL,"
	   "xuser_reprint ENUM(\"IN FILE\",\"NOT IN FILE\",\"ON REQUEST\"),"
	   "xuser_date DATE,"
	   "xuser_avail VARCHAR(255),"
	   "xuser_notes MEDIUMTEXT,"
	   "PRIMARY KEY (xuser_id),"
	   "KEY (user_id),"
	   "KEY (refdb_id))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(220));
    return 220;
  }
   
  dbi_result_free(dbires);

  /* create the notes xtable */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxnote ("
	   "xnote_id BIGINT NOT NULL AUTO_INCREMENT,"
	   "note_id BIGINT NOT NULL,"
	   "xref_id BIGINT NOT NULL,"
	   "xnote_type ENUM(\"REFERENCE\",\"KEYWORD\",\"AUTHOR\",\"PERIODICAL\"),"
	   "PRIMARY KEY (xnote_id),"
	   "KEY (note_id),"
	   "KEY (xref_id),"
	   "KEY (xnote_type))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(221));
    return 221;
  }
   
  dbi_result_free(dbires);

  /* create the link xtable */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxlink ("
	   "xlink_id BIGINT NOT NULL AUTO_INCREMENT,"
	   "link_id BIGINT NOT NULL,"
	   "xref_id BIGINT NOT NULL,"
	   "user_id BIGINT DEFAULT 0,"
	   "xlink_type ENUM(\"URL\",\"PDF\",\"FULLTEXT\",\"RELATED\", \"IMAGE\", \"DOI\"),"
	   "xlink_source ENUM(\"REFERENCE\",\"NOTE\"),"
	   "PRIMARY KEY (xlink_id),"
	   "KEY (link_id),"
	   "KEY (xref_id))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(258));
    return 258;
  }
   
  dbi_result_free(dbires);
  free(sql_command);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  create_xdup_tables_mysql(): creates MySQL-specific duplicate check tables

  int create_xdup_tables_mysql returns >0 if error, 0 if successful 

  dbi_conn conn database connection structure

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int create_xdup_tables_mysql(dbi_conn conn) {
  char* sql_command;
  size_t sql_command_len = 512;
  dbi_result dbires;

  /* get us some memory for the query strings */
  if ((sql_command = malloc(sql_command_len)) == NULL) {
    return 801;
  }
  sprintf(sql_command, "CREATE TEMPORARY TABLE t_temp_xdup ("
	  "xdup_id  BIGINT NOT NULL AUTO_INCREMENT,"
	  "xdup_type ENUM (\"TITLE\",\"LOCATION\",\"AUTHOR\",\'CITEKEY\',\'KEYWORD\',\'PERIODICAL\'),"
	  "match_type ENUM (\"IDENT\",\"CASE\",\"LIKE\",\"ABBREV\"),"
	  "temp_refdb_id BIGINT,"
	  "refdb_id BIGINT,"
	  "value_name TEXT,"
	  "temp_value_name TEXT,"
	  "value_name_2 TEXT,"
	  "temp_value_name_2 TEXT,"
	  "value_name_3 TEXT,"
	  "temp_value_name_3 TEXT,"
	  "PRIMARY KEY (xdup_id))");
  
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);
  free(sql_command);

  if (!dbires) {
    LOG_PRINT(LOG_ERR, get_status_msg(268));
    return 268;
  }
   
  dbi_result_free(dbires);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  create_tables_pgsql(): creates PostgreSQL-specific reference data tables

  int create_tables_pgsql returns >0 if error, 0 if successful 

  dbi_conn conn database connection structure

  struct CLIENT_REQUEST* ptr_clrequest ptr to structure with client info

  int is_temp if nonzero, create a temporary table, otherwise a permanent table

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int create_tables_pgsql(dbi_conn conn, struct CLIENT_REQUEST* ptr_clrequest, int is_temp) {
  char* sql_command;
  char buffer[512];
  char temporary[] = TEMP_TABLE_SPECIFIER;
  char prefix[] = TEMP_TABLE_NAME_PREFIX;
  size_t sql_command_len = 2048;
  dbi_result dbires;

  /* get us some memory for the query strings */
  if ((sql_command = malloc(sql_command_len)) == NULL) {
    return 801;
  }

  /* fix the inserts */
  if (!is_temp) {
    *temporary = '\0';
    *prefix = '\0';
  }


  /* create the metadata table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%smeta ("
	   "meta_app VARCHAR(20),"
	   "meta_type VARCHAR(20),"
	   "meta_version VARCHAR(20),"
	   "meta_dbversion SMALLINT,"
	   "meta_create_date TIMESTAMP,"
	   "meta_modify_date TIMESTAMP)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(211));
    return 211;
  }
   
  dbi_result_free(dbires);

  /* create the main table */
  /* using a DATE field for refdb_pubyear is not possible as MySQL < 3.23
     does not allow to store dates like 2000-00-00, i.e. when only the
     year is known */
  /* refdb_periodical_id is actually a FOREIGN KEY, but MySQL does not
     support this so a simple INT has to do the trick */

  /* this will implicitly create a SEQUENCE t_refdb_refdb_id_seq */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%srefdb ("
	   "refdb_id BIGSERIAL,"
	   "refdb_citekey VARCHAR(255) UNIQUE,"
	   "refdb_type VARCHAR(6),"
	   "refdb_pubyear SMALLINT,"
	   "refdb_secyear SMALLINT,"
	   "refdb_startpage VARCHAR(255),"
	   "refdb_endpage VARCHAR(255),"
	   "refdb_abstract TEXT,"
	   "refdb_title TEXT,"
	   "refdb_volume VARCHAR(255),"
	   "refdb_issue VARCHAR(255),"
	   "refdb_booktitle TEXT,"
	   "refdb_city VARCHAR(255),"
	   "refdb_publisher VARCHAR(255),"
	   "refdb_title_series TEXT,"
	   "refdb_address TEXT,"
	   "refdb_issn VARCHAR(255),"
	   "refdb_pyother_info VARCHAR(255),"
	   "refdb_secother_info VARCHAR(255),"
	   "refdb_periodical_id BIGINT,"
	   "refdb_user1 VARCHAR(255),"
	   "refdb_user2 VARCHAR(255),"
	   "refdb_user3 VARCHAR(255),"
	   "refdb_user4 VARCHAR(255),"
	   "refdb_user5 VARCHAR(255),"
	   "refdb_typeofwork VARCHAR(255),"
	   "refdb_area VARCHAR(255),"
	   "refdb_ostype VARCHAR(255),"
	   "refdb_degree VARCHAR(255),"
	   "refdb_runningtime VARCHAR(255),"
	   "refdb_classcodeintl VARCHAR(255),"
	   "refdb_classcodeus VARCHAR(255),"
	   "refdb_senderemail VARCHAR(255),"
	   "refdb_recipientemail VARCHAR(255),"
	   "refdb_mediatype VARCHAR(255),"
	   "refdb_numvolumes VARCHAR(255),"
	   "refdb_edition VARCHAR(255),"
	   "refdb_computer VARCHAR(255),"
	   "refdb_conferencelocation VARCHAR(255),"
	   "refdb_registrynum VARCHAR(255),"
	   "refdb_classification VARCHAR(255),"
	   "refdb_section VARCHAR(255),"
	   "refdb_pamphletnum VARCHAR(255),"
	   "refdb_chapternum VARCHAR(255),"
	   "PRIMARY KEY (refdb_id))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(212));
    return 212;
  }
   
  dbi_result_free(dbires);
  
  /* create the periodical index */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%spubyear ON t_%srefdb (refdb_pubyear)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(212));
    return 212;
  }
   
  dbi_result_free(dbires);

  /* create the author table */
  /* this will implicitly create a SEQUENCE t_author_author_id_seq */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sauthor ("
	   "author_id BIGSERIAL,"
	   "author_name VARCHAR(255) NOT NULL,"
	   "author_lastname VARCHAR(255),"
	   "author_firstname VARCHAR(255),"
	   "author_middlename VARCHAR(255),"
	   "author_suffix VARCHAR(255),"
	   "PRIMARY KEY (author_id))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(213));
    return 213;
  }
   
  dbi_result_free(dbires);

  /* create the author_name index */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sauthor_name ON t_%sauthor (author_name)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(213));
    return 213;
  }
   
  dbi_result_free(dbires);

  /* create the keyword table */
  /* this will implicitly create a SEQUENCE t_keyword_keyword_id_seq */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%skeyword ("
	   "keyword_id BIGSERIAL,"
	   "keyword_name VARCHAR(255) NOT NULL,"
	   "PRIMARY KEY (keyword_id))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);
  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(214));
    return 214;
  }
   
  dbi_result_free(dbires);

  /* create the keyword_name index */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%skeyword_name ON t_%skeyword (keyword_name)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(214));
    return 214;
  }
   
  dbi_result_free(dbires);

  /* create the periodical table */
  /* this will implicitly create a SEQUENCE t_periodical_periodical_id_seq */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%speriodical ("
	   "periodical_id BIGSERIAL,"
	   "periodical_name VARCHAR(255),"
	   "periodical_abbrev VARCHAR(255),"
	   "periodical_custabbrev1 VARCHAR(255),"
	   "periodical_custabbrev2 VARCHAR(255),"
	   "PRIMARY KEY (periodical_id))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(215));
    return 215;
  }
   
  dbi_result_free(dbires);

  /* create the t_periodical indices */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%speriodical_name ON t_%speriodical (periodical_name)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(215));
    return 215;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%speriodical_abbrev ON t_%speriodical (periodical_abbrev)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(215));
    return 215;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%speriodical_custabbrev1 ON t_%speriodical (periodical_custabbrev1)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(215));
    return 215;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i%s_periodical_custabbrev2 ON t_%speriodical (periodical_custabbrev2)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);
    
  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(215));
    return 215;
  }
   
  dbi_result_free(dbires);

  /* create the notes table */
  /* this will implicitly create a SEQUENCE t_note_note_id_seq */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%snote ("
	   "note_id BIGSERIAL,"
	   "note_key VARCHAR(255) UNIQUE,"
	   "note_title VARCHAR(255),"
	   "note_content TEXT,"
	   "note_content_type VARCHAR(255),"
	   "note_content_xmllang VARCHAR(255),"
	   "note_user_id BIGINT,"
	   "note_date DATE,"
	   "note_share SMALLINT,"
	   "PRIMARY KEY (note_id))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(216));
    return 216;
  }
   
  dbi_result_free(dbires);

  /* create the t_note indexes */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%snote_title ON t_%snote (note_title)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);
  
  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(216));
    return 216;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%snote_note_user_id ON t_%snote (note_user_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);
    
  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(216));
    return 216;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%snote_date ON t_%snote (note_date)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(216));
    return 216;
  }
   
  dbi_result_free(dbires);

  /* create the user table */
  /* the user_name length limit is imposed by MySQL, don't know about PostgreSQL */
  /* this will implicitly create a SEQUENCE t_user_user_id_seq */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%suser ("
	   "user_id BIGSERIAL,"
	   "user_name VARCHAR(16) NOT NULL,"
	   "PRIMARY KEY (user_id))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(217));
    return 217;
  }
   
  dbi_result_free(dbires);

  /* create user_name index */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%suser_name ON t_%suser (user_name)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(217));
    return 217;
  }
   
  dbi_result_free(dbires);

  /* create the link table */

  /* this will implicitly create a SEQUENCE t_link_link_id_seq */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%slink ("
	   "link_id BIGSERIAL,"
	   "link_url TEXT NOT NULL,"
	   "PRIMARY KEY (link_id))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(257));
    return 257;
  }
   
  dbi_result_free(dbires);

  /* create link_url index */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%slink_url ON t_%slink (link_url)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(257));
    return 257;
  }
   
  dbi_result_free(dbires);

  /* create the author xtable */
  /* this will implicitly create a SEQUENCE t_xauthor_xauthor_id_seq */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxauthor ("
	   "xauthor_id BIGSERIAL,"
	   "author_id BIGINT NOT NULL,"
	   "refdb_id BIGINT NOT NULL,"
	   "xauthor_type SMALLINT DEFAULT 1,"
	   "xauthor_role VARCHAR(64),"
	   "xauthor_position INTEGER,"
	   "PRIMARY KEY (xauthor_id))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(218));
    return 218;
  }
   
  dbi_result_free(dbires);

  /* create x_author indices */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxauthor_author_id ON t_%sxauthor (author_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(218));
    return 218;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxauthor_refdb_id ON t_%sxauthor (refdb_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);
    
  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(218));
    return 218;
  }
   
  dbi_result_free(dbires);

  /* create the keyword xtable */
  /* this will implicitly create a SEQUENCE t_xkeyword_xkeyword_id_seq */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxkeyword ("
	   "xkeyword_id BIGSERIAL,"
	   "keyword_id BIGINT NOT NULL,"
	   "xkeyword_type VARCHAR(11) DEFAULT \'REFERENCE\',"
	   "xref_id BIGINT NOT NULL,"
	   "PRIMARY KEY (xkeyword_id))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(219));
    return 219;
  }
   
  dbi_result_free(dbires);

  /* create x_keyword indices */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxkeyword_keyword_id ON t_%sxkeyword (keyword_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(219));
    return 219;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxkeyword_xref_id ON t_%sxkeyword (xref_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);
    
  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(219));
    return 219;
  }
   
  dbi_result_free(dbires);

  /* create the user xtable */
  /* this will implicitly create a SEQUENCE t_xuser_xuser_id_seq */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxuser ("
	   "xuser_id BIGSERIAL,"
	   "user_id BIGINT NOT NULL,"
	   "refdb_id BIGINT NOT NULL,"
	   "xuser_reprint VARCHAR(11) DEFAULT \'NOT IN FILE\',"
	   "xuser_date DATE,"
	   "xuser_avail VARCHAR(255),"
	   "xuser_notes TEXT,"
	   "PRIMARY KEY (xuser_id))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(220));
    return 220;
  }
   
  dbi_result_free(dbires);

  /* create x_user indices */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxuser_user_id ON t_%sxuser (user_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(220));
    return 220;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxuser_refdb_id ON t_%sxuser (refdb_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);
    
  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(220));
    return 220;
  }
   
  dbi_result_free(dbires);

  /* create the notes xtable */
  /* this will implicitly create a SEQUENCE t_xnote_xnote_id_seq */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxnote ("
	   "xnote_id BIGSERIAL,"
	   "note_id BIGINT NOT NULL,"
	   "xref_id BIGINT NOT NULL,"
	   "xnote_type VARCHAR(11) DEFAULT \'REFERENCE\',"
	   "PRIMARY KEY (xnote_id))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(221));
    return 221;
  }
   
  dbi_result_free(dbires);

  /* create x_note indices */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxnote_note_id ON t_%sxnote (note_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);
  
  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(221));
    return 221;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxnote_xref_id ON t_%sxnote (xref_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(221));
    return 221;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxnote_xnote_type ON t_%sxnote (xnote_type)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(221));
    return 221;
  }
   
  dbi_result_free(dbires);

  /* create the links xtable */
  /* this will implicitly create a SEQUENCE t_xlink_xlink_id_seq */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxlink ("
	   "xlink_id BIGSERIAL,"
	   "link_id BIGINT NOT NULL,"
	   "xref_id BIGINT NOT NULL,"
	   "user_id BIGINT DEFAULT 0,"
	   "xlink_type VARCHAR(11) DEFAULT \'URL\',"
	   "xlink_source VARCHAR(11) DEFAULT \'REFERENCE\',"
	   "PRIMARY KEY (xlink_id))", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(258));
    return 258;
  }
   
  dbi_result_free(dbires);

  /* create x_link indices */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxlink_link_id ON t_%sxlink (link_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(258));
    return 258;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxlink_xref_id ON t_%sxlink (xref_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(258));
    return 258;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxlink_xlink_type ON t_%sxlink (xlink_type)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(258));
    return 258;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxlink_xlink_source ON t_%sxlink (xlink_source)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(258));
    return 258;
  }
   
  dbi_result_free(dbires);

  /* create user permissions */
  if (!is_temp) {
    /* PostgreSQL uses table-based permissions, so it is easier to create
       them here than in adduser. We grant read/write permissions to a
       group "<dbname>user" and read permissions to "<dbname>ruser"
       here, whereas adduser just adds users to/removes from this group */

    /* ptr_clrequest->argument contains the database name */
    if (is_group_pgsql(conn, ptr_clrequest->argument, 0 /* read/write */) != 1) {
      sprintf(buffer, "CREATE GROUP %suser", ptr_clrequest->argument);
      LOG_PRINT(LOG_DEBUG, buffer);
    
      dbires = dbi_conn_query(conn, buffer);

      if (!dbires) {
	free(sql_command);
	LOG_PRINT(LOG_ERR, get_status_msg(222));
	return 222;
      }

      dbires = dbi_conn_query(conn, sql_command);
    }

    sprintf(buffer, "GRANT SELECT, INSERT, UPDATE, DELETE ON t_meta, t_refdb, t_author, t_keyword, t_periodical, t_user, t_note, t_link, t_xauthor, t_xkeyword, t_xuser, t_xnote, t_xlink, t_refdb_refdb_id_seq, t_author_author_id_seq, t_keyword_keyword_id_seq, t_periodical_periodical_id_seq, t_user_user_id_seq, t_note_note_id_seq, t_link_link_id_seq, t_xauthor_xauthor_id_seq, t_xkeyword_xkeyword_id_seq, t_xuser_xuser_id_seq, t_xnote_xnote_id_seq, t_xlink_xlink_id_seq TO GROUP %suser", ptr_clrequest->argument);
    LOG_PRINT(LOG_DEBUG, buffer);

    dbires = dbi_conn_query(conn, buffer);

    if (!dbires) {
      free(sql_command);
      LOG_PRINT(LOG_ERR, get_status_msg(223));
      return 223;
    }
   
    dbi_result_free(dbires);

    if (is_group_pgsql(conn, ptr_clrequest->argument, 1 /* read-only */) != 1) {
      sprintf(buffer, "CREATE GROUP %sruser", ptr_clrequest->argument);
      LOG_PRINT(LOG_DEBUG, buffer);
    
      dbires = dbi_conn_query(conn, buffer);

      if (!dbires) {
	free(sql_command);
	LOG_PRINT(LOG_ERR, get_status_msg(222));
	return 222;
      }

      dbires = dbi_conn_query(conn, sql_command);
    }

    sprintf(buffer, "GRANT SELECT ON t_meta, t_refdb, t_author, t_keyword, t_periodical, t_user, t_note, t_link, t_xauthor, t_xkeyword, t_xuser, t_xnote, t_xlink, t_refdb_refdb_id_seq, t_author_author_id_seq, t_keyword_keyword_id_seq, t_periodical_periodical_id_seq, t_user_user_id_seq, t_note_note_id_seq, t_link_link_id_seq, t_xauthor_xauthor_id_seq, t_xkeyword_xkeyword_id_seq, t_xuser_xuser_id_seq, t_xnote_xnote_id_seq, t_xlink_xlink_id_seq TO GROUP %sruser", ptr_clrequest->argument);
    LOG_PRINT(LOG_DEBUG, buffer);

    dbires = dbi_conn_query(conn, buffer);

    if (!dbires) {
      free(sql_command);
      LOG_PRINT(LOG_ERR, get_status_msg(223));
      return 223;
    }
   
    dbi_result_free(dbires);
  }

  free(sql_command);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  create_xdup_tables_pgsql(): creates PostgreSQL-specific duplicate
                              check tables

  int create_xdup_tables_pgsql returns >0 if error, 0 if successful 

  dbi_conn conn database connection structure

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int create_xdup_tables_pgsql(dbi_conn conn) {
  char* sql_command;
  size_t sql_command_len = 512;
  dbi_result dbires;

  /* get us some memory for the query strings */
  if ((sql_command = malloc(sql_command_len)) == NULL) {
    return 801;
  }
  sprintf(sql_command, "CREATE TEMPORARY TABLE t_temp_xdup ("
	  "xdup_id  BIGSERIAL,"
	  "xdup_type VARCHAR(10),"
	  "match_type VARCHAR(10),"
	  "temp_refdb_id BIGINT,"
	  "refdb_id BIGINT,"
	  "value_name TEXT,"
	  "temp_value_name TEXT,"
	  "value_name_2 TEXT,"
	  "temp_value_name_2 TEXT,"
	  "value_name_3 TEXT,"
	  "temp_value_name_3 TEXT,"
	  "PRIMARY KEY (xdup_id))");
  
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  free(sql_command);

  if (!dbires) {
    LOG_PRINT(LOG_ERR, get_status_msg(268));
    return 268;
  }
   
  dbi_result_free(dbires);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  create_tables_sqlite(): creates SQLite-specific reference data tables

  int create_tables_sqlite returns >0 if error, 0 if successful 

  dbi_conn conn database connection structure

  struct CLIENT_REQUEST* ptr_clrequest ptr to structure with client info

  int is_temp if nonzero, create a temporary table, otherwise a permanent table

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int create_tables_sqlite(dbi_conn conn, struct CLIENT_REQUEST* ptr_clrequest, int is_temp) {
  char* sql_command;
  char temporary[] = TEMP_TABLE_SPECIFIER;
  char prefix[] = TEMP_TABLE_NAME_PREFIX;
  size_t sql_command_len = 2048;
  dbi_result dbires;

  /* get us some memory for the query strings */
  if ((sql_command = malloc(sql_command_len)) == NULL) {
    return 801;
  }

  /* fix the inserts */
  if (!is_temp) {
    *temporary = '\0';
    *prefix = '\0';
  }

  /* create the metadata table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%smeta ("
	   "meta_app VARCHAR(20),"
	   "meta_type VARCHAR(20),"
	   "meta_version VARCHAR(20),"
	   "meta_dbversion SMALLINT,"
	   "meta_create_date DATETIME,"
	   "meta_modify_date DATETIME)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(211));
    return 211;
  }
   
  dbi_result_free(dbires);

  /* create the main table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%srefdb ("
	   "refdb_id INTEGER PRIMARY KEY,"
	   "refdb_citekey TEXT UNIQUE,"
	   "refdb_type TEXT,"
	   "refdb_pubyear SMALLINT,"
	   "refdb_secyear SMALLINT,"
	   "refdb_startpage TEXT,"
	   "refdb_endpage TEXT,"
	   "refdb_abstract TEXT,"
	   "refdb_title TEXT,"
	   "refdb_volume TEXT,"
	   "refdb_issue TEXT,"
	   "refdb_booktitle TEXT,"
	   "refdb_city TEXT,"
	   "refdb_publisher TEXT,"
	   "refdb_title_series TEXT,"
	   "refdb_address TEXT,"
	   "refdb_issn TEXT,"
	   "refdb_pyother_info TEXT,"
	   "refdb_secother_info TEXT,"
	   "refdb_periodical_id INTEGER,"
	   "refdb_user1 TEXT,"
	   "refdb_user2 TEXT,"
	   "refdb_user3 TEXT,"
	   "refdb_user4 TEXT,"
	   "refdb_user5 TEXT,"
	   "refdb_typeofwork TEXT,"
	   "refdb_area TEXT,"
	   "refdb_ostype TEXT,"
	   "refdb_degree TEXT,"
	   "refdb_runningtime TEXT,"
	   "refdb_classcodeintl TEXT,"
	   "refdb_classcodeus TEXT,"
	   "refdb_senderemail TEXT,"
	   "refdb_recipientemail TEXT,"
	   "refdb_mediatype TEXT,"
	   "refdb_numvolumes TEXT,"
	   "refdb_edition TEXT,"
	   "refdb_computer TEXT,"
	   "refdb_conferencelocation TEXT,"
	   "refdb_registrynum TEXT,"
	   "refdb_classification TEXT,"
	   "refdb_section TEXT,"
	   "refdb_pamphletnum TEXT,"
	   "refdb_chapternum TEXT)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(212));
    return 212;
  }
   
  dbi_result_free(dbires);

  /* create the main table indexes */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%srefdb_pubyear ON t_%srefdb (refdb_pubyear)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(212));
    return 212;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE UNIQUE INDEX i_%srefdb_citekey ON t_%srefdb (refdb_citekey)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(212));
    return 212;
  }
   
  dbi_result_free(dbires);

  /* create the author table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sauthor ("
			  "author_id INTEGER PRIMARY KEY,"
			  "author_name TEXT NOT NULL,"
			  "author_lastname TEXT,"
			  "author_firstname TEXT,"
			  "author_middlename TEXT,"
			  "author_suffix TEXT)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(213));
    return 213;
  }
   
  dbi_result_free(dbires);

  /* create t_author table index */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sauthor_name ON t_%sauthor (author_name)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(213));
    return 213;
  }
   
  dbi_result_free(dbires);

  /* create the keyword table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%skeyword ("
			  "keyword_id INTEGER PRIMARY KEY,"
			  "keyword_name TEXT NOT NULL)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(214));
    return 214;
  }
   
  dbi_result_free(dbires);

  /* create t_keyword table index */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%skeyword_name ON t_%skeyword (keyword_name)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(214));
    return 214;
  }
   
  dbi_result_free(dbires);

  /* create the periodical table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%speriodical ("
			  "periodical_id INTEGER PRIMARY KEY,"
			  "periodical_name TEXT,"
			  "periodical_abbrev TEXT,"
			  "periodical_custabbrev1 TEXT,"
			  "periodical_custabbrev2 TEXT)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(215));
    return 215;
  }
   
  dbi_result_free(dbires);

  /* create t_periodical table indexes */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%speriodical_name ON t_%speriodical (periodical_name)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(215));
    return 215;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%speriodical_abbrev ON t_%speriodical (periodical_abbrev)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(215));
    return 215;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%speriodical_custabbrev1 ON t_%speriodical (periodical_custabbrev1)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(215));
    return 215;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%speriodical_custabbrev2 ON t_%speriodical (periodical_custabbrev2)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(215));
    return 215;
  }
   
  dbi_result_free(dbires);

  /* create the notes table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%snote ("
			  "note_id INTEGER PRIMARY KEY,"
			  "note_key TEXT UNIQUE,"
			  "note_title TEXT,"
			  "note_content_type TEXT,"
			  "note_content_xmllang TEXT,"
			  "note_user_id INTEGER,"
			  "note_date DATE,"
			  "note_share SMALLINT,"
			  "note_content TEXT)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(216));
    return 216;
  }
   
  dbi_result_free(dbires);

  /* create t_note table indexes */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%snote_title ON t_%snote (note_title)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(216));
    return 216;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%snote_note_userid ON t_%snote (note_user_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(216));
    return 216;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%snote_date ON t_%snote (note_date)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(216));
    return 216;
  }
   
  dbi_result_free(dbires);

  /* create the user table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%suser ("
			  "user_id INTEGER PRIMARY KEY,"
			  "user_name TEXT NOT NULL)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(217));
    return 217;
  }
   
  dbi_result_free(dbires);

  /* create t_user table index */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%suser_name ON t_%suser (user_name)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(217));
    return 217;
  }
   
  dbi_result_free(dbires);

  /* create the link table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%slink ("
			  "link_id INTEGER PRIMARY KEY,"
			  "link_url TEXT NOT NULL)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(257));
    return 257;
  }
   
  dbi_result_free(dbires);

  /* create t_link table index */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%slink_url ON t_%slink (link_url)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(257));
    return 257;
  }
   
  dbi_result_free(dbires);

  /* create the author xtable */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxauthor ("
			  "xauthor_id INTEGER PRIMARY KEY,"
			  "author_id INTEGER NOT NULL,"
			  "refdb_id INTEGER NOT NULL,"
			  "xauthor_type SMALLINT DEFAULT 1,"
			  "xauthor_role TEXT,"
			  "xauthor_position INTEGER)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(218));
    return 218;
  }
   
  dbi_result_free(dbires);

  /* create t_xauthor table indexes */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxauthor_autid ON t_%sxauthor (author_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(218));
    return 218;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxauthor_refid ON t_%sxauthor (refdb_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(218));
    return 218;
  }
   
  dbi_result_free(dbires);

  /* create the keyword xtable */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxkeyword ("
			  "xkeyword_id INTEGER PRIMARY KEY,"
			  "xkeyword_type TEXT,"
			  "keyword_id INTEGER NOT NULL,"
			  "xref_id INTEGER NOT NULL)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    LOG_PRINT(LOG_ERR, get_status_msg(219));
    return 219;
  }
   
  dbi_result_free(dbires);

  /* create t_xkeyword table indexes */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxkeyword_kwid ON t_%sxkeyword (keyword_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(219));
    return 219;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxkeyword_xrefid ON t_%sxkeyword (xref_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(219));
    return 219;
  }
   
  dbi_result_free(dbires);

  /* create the user xtable */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxuser ("
			  "xuser_id INTEGER PRIMARY KEY,"
			  "user_id INTEGER NOT NULL,"
			  "refdb_id INTEGER NOT NULL,"
			  "xuser_reprint TEXT DEFAULT \'NOT IN FILE\',"
			  "xuser_date DATE,"
			  "xuser_avail TEXT,"
			  "xuser_notes TEXT)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(220));
    return 220;
  }
   
  dbi_result_free(dbires);

  /* create t_xuser table indexes */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxuser_userid ON t_%sxuser (user_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(220));
    return 220;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxuser_refid ON t_%sxuser (refdb_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(220));
    return 220;
  }
   
  dbi_result_free(dbires);

  /* create the notes xtable */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxnote ("
			  "xnote_id INTEGER PRIMARY KEY,"
			  "note_id INTEGER NOT NULL,"
			  "xref_id INTEGER NOT NULL,"
			  "xnote_type TEXT DEFAULT \'REFERENCE\')", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(221));
    return 221;
  }
   
  dbi_result_free(dbires);

  /* create t_xnote table indexes */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxnote_noteid ON t_%sxnote (note_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(221));
    return 221;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxnote_xrefid ON t_%sxnote (xref_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(221));
    return 221;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxnote_xnotetype ON t_%sxnote (xnote_type)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(221));
    return 221;
  }
   
  dbi_result_free(dbires);

  /* create the links xtable */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxlink ("
			  "xlink_id INTEGER PRIMARY KEY,"
			  "link_id INTEGER NOT NULL,"
			  "xref_id INTEGER NOT NULL,"
			  "user_id INTEGER DEFAULT 0,"
			  "xlink_type TEXT DEFAULT \'URL\',"
			  "xlink_source TEXT DEFAULT \'REFERENCE\')", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(258));
    return 258;
  }
   
  dbi_result_free(dbires);

  /* create t_xlink table indexes */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxlink_linkid ON t_%sxlink (link_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(258));
    return 258;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxlink_xrefid ON t_%sxlink (xref_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(258));
    return 258;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxlink_xlinktype ON t_%sxlink (xlink_type)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(258));
    return 258;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxlink_xlinksource ON t_%sxlink (xlink_source)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(258));
    return 258;
  }
   
  dbi_result_free(dbires);
  free(sql_command);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  create_xdup_tables_sqlite(): creates SQLite-specific duplicate check tables

  int create_xdup_tables_sqlite returns >0 if error, 0 if successful 

  dbi_conn conn database connection structure

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int create_xdup_tables_sqlite(dbi_conn conn) {
  char* sql_command;
  size_t sql_command_len = 512;
  dbi_result dbires;

  /* get us some memory for the query strings */
  if ((sql_command = malloc(sql_command_len)) == NULL) {
    return 801;
  }
  sprintf(sql_command, "CREATE TEMPORARY TABLE t_temp_xdup ("
	  "xdup_id  INTEGER PRIMARY KEY,"
	  "xdup_type VARCHAR(10),"
	  "match_type VARCHAR(10),"
	  "temp_refdb_id INTEGER,"
	  "refdb_id INTEGER,"
	  "value_name TEXT,"
	  "temp_value_name TEXT,"
	  "value_name_2 TEXT,"
	  "temp_value_name_2 TEXT,"
	  "value_name_3 TEXT,"
	  "temp_value_name_3 TEXT)");
  
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);
  free(sql_command);

  if (!dbires) {
    LOG_PRINT(LOG_ERR, get_status_msg(268));
    return 268;
  }
   
  dbi_result_free(dbires);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  create_tables_sqlite3(): creates SQLite3-specific reference data tables

  int create_tables_sqlite3 returns >0 if error, 0 if successful 

  struct CLIENT_REQUEST* ptr_clrequest ptr to structure with client info

  int is_temp if nonzero, create a temporary table, otherwise a permanent table

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int create_tables_sqlite3(dbi_conn conn, struct CLIENT_REQUEST* ptr_clrequest, int is_temp) {
  char* sql_command;
  char temporary[] = TEMP_TABLE_SPECIFIER;
  char prefix[] = TEMP_TABLE_NAME_PREFIX;
  size_t sql_command_len = 2048;
  dbi_result dbires;

  /* get us some memory for the query strings */
  if ((sql_command = malloc(sql_command_len)) == NULL) {
    return 801;
  }

  /* fix the inserts */
  if (!is_temp) {
    *temporary = '\0';
    *prefix = '\0';
  }

  /* create the metadata table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%smeta ("
	   "meta_app VARCHAR(20),"
	   "meta_type VARCHAR(20),"
	   "meta_version VARCHAR(20),"
	   "meta_dbversion SMALLINT,"
	   "meta_create_date DATETIME,"
	   "meta_modify_date DATETIME)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);
  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(211));
    return 211;
  }
   
  dbi_result_free(dbires);

  /* create the main table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%srefdb ("
	   "refdb_id INTEGER PRIMARY KEY,"
	   "refdb_citekey TEXT UNIQUE,"
	   "refdb_type TEXT,"
	   "refdb_pubyear SMALLINT,"
	   "refdb_secyear SMALLINT,"
	   "refdb_startpage TEXT,"
	   "refdb_endpage TEXT,"
	   "refdb_abstract TEXT,"
	   "refdb_title TEXT,"
	   "refdb_volume TEXT,"
	   "refdb_issue TEXT,"
	   "refdb_booktitle TEXT,"
	   "refdb_city TEXT,"
	   "refdb_publisher TEXT,"
	   "refdb_title_series TEXT,"
	   "refdb_address TEXT,"
	   "refdb_issn TEXT,"
	   "refdb_pyother_info TEXT,"
	   "refdb_secother_info TEXT,"
	   "refdb_periodical_id BIGINT,"
	   "refdb_user1 TEXT,"
	   "refdb_user2 TEXT,"
	   "refdb_user3 TEXT,"
	   "refdb_user4 TEXT,"
	   "refdb_user5 TEXT,"
	   "refdb_typeofwork TEXT,"
	   "refdb_area TEXT,"
	   "refdb_ostype TEXT,"
	   "refdb_degree TEXT,"
	   "refdb_runningtime TEXT,"
	   "refdb_classcodeintl TEXT,"
	   "refdb_classcodeus TEXT,"
	   "refdb_senderemail TEXT,"
	   "refdb_recipientemail TEXT,"
	   "refdb_mediatype TEXT,"
	   "refdb_numvolumes TEXT,"
	   "refdb_edition TEXT,"
	   "refdb_computer TEXT,"
	   "refdb_conferencelocation TEXT,"
	   "refdb_registrynum TEXT,"
	   "refdb_classification TEXT,"
	   "refdb_section TEXT,"
	   "refdb_pamphletnum TEXT,"
	   "refdb_chapternum TEXT)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(212));
    return 212;
  }
   
  dbi_result_free(dbires);

  /* create the main table indexes */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%srefdb_pubyear ON t_%srefdb (refdb_pubyear)" , prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(212));
    return 212;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE UNIQUE INDEX i_%srefdb_citekey ON t_%srefdb (refdb_citekey)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(212));
    return 212;
  }
   
  dbi_result_free(dbires);

  /* create the author table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sauthor ("
			  "author_id INTEGER PRIMARY KEY,"
			  "author_name TEXT NOT NULL,"
			  "author_lastname TEXT,"
			  "author_firstname TEXT,"
			  "author_middlename TEXT,"
			  "author_suffix TEXT)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(213));
    return 213;
  }
   
  dbi_result_free(dbires);

  /* create t_author table index */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sauthor_name ON t_%sauthor (author_name)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(213));
    return 213;
  }
   
  dbi_result_free(dbires);

  /* create the keyword table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%skeyword ("
			  "keyword_id INTEGER PRIMARY KEY,"
			  "keyword_name TEXT NOT NULL)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(214));
    return 214;
  }
   
  dbi_result_free(dbires);

  /* create t_keyword table index */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%skeyword_name ON t_%skeyword (keyword_name)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(214));
    return 214;
  }
   
  dbi_result_free(dbires);

  /* create the periodical table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%speriodical ("
			  "periodical_id INTEGER PRIMARY KEY,"
			  "periodical_name TEXT,"
			  "periodical_abbrev TEXT,"
			  "periodical_custabbrev1 TEXT,"
			  "periodical_custabbrev2 TEXT)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(215));
    return 215;
  }
   
  dbi_result_free(dbires);

  /* create t_periodical table indexes */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%speriodical_name ON t_%speriodical (periodical_name)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(215));
    return 215;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%speriodical_abbrev ON t_%speriodical (periodical_abbrev)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(215));
    return 215;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%speriodical_custabbrev1 ON t_%speriodical (periodical_custabbrev1)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(215));
    return 215;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%speriodical_custabbrev2 ON t_%speriodical (periodical_custabbrev2)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(215));
    return 215;
  }
   
  dbi_result_free(dbires);

  /* create the notes table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%snote ("
			  "note_id INTEGER PRIMARY KEY,"
			  "note_key TEXT UNIQUE,"
			  "note_title TEXT,"
			  "note_content_type TEXT,"
			  "note_content_xmllang TEXT,"
			  "note_user_id BIGINT,"
			  "note_date DATE,"
			  "note_share SMALLINT,"
			  "note_content TEXT)" , temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(216));
    return 216;
  }
   
  dbi_result_free(dbires);

  /* create t_note table indexes */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%snote_title ON t_%snote (note_title)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(216));
    return 216;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%snote_note_userid ON t_%snote (note_user_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(216));
    return 216;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%snote_date ON t_%snote (note_date)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(216));
    return 216;
  }
   
  dbi_result_free(dbires);

  /* create the user table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%suser ("
			  "user_id INTEGER PRIMARY KEY,"
			  "user_name TEXT NOT NULL)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(217));
    return 217;
  }
   
  dbi_result_free(dbires);

  /* create t_user table index */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%suser_name ON t_%suser (user_name)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(217));
    return 217;
  }
   
  dbi_result_free(dbires);

  /* create the link table */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%slink ("
			  "link_id INTEGER PRIMARY KEY,"
			  "link_url TEXT NOT NULL)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(257));
    return 257;
  }
   
  dbi_result_free(dbires);

  /* create t_link table index */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%slink_url ON t_%slink (link_url)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(257));
    return 257;
  }
   
  dbi_result_free(dbires);

  /* create the author xtable */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxauthor ("
			  "xauthor_id INTEGER PRIMARY KEY,"
			  "author_id BIGINT NOT NULL,"
			  "refdb_id BIGINT NOT NULL,"
			  "xauthor_type SMALLINT DEFAULT 1,"
			  "xauthor_role TEXT,"
			  "xauthor_position INTEGER)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(218));
    return 218;
  }
   
  dbi_result_free(dbires);

  /* create t_xauthor table indexes */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxauthor_autid ON t_%sxauthor (author_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(218));
    return 218;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxauthor_refid ON t_%sxauthor (refdb_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(218));
    return 218;
  }
   
  dbi_result_free(dbires);

  /* create the keyword xtable */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxkeyword ("
			  "xkeyword_id INTEGER PRIMARY KEY,"
			  "xkeyword_type TEXT,"
			  "keyword_id BIGINT NOT NULL,"
			  "xref_id BIGINT NOT NULL)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(219));
    return 219;
  }
   
  dbi_result_free(dbires);

  /* create t_xkeyword table indexes */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxkeyword_kwid ON t_%sxkeyword (keyword_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(219));
    return 219;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxkeyword_xrefid ON t_%sxkeyword (xref_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(219));
    return 219;
  }
   
  dbi_result_free(dbires);

  /* create the user xtable */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxuser ("
			  "xuser_id INTEGER PRIMARY KEY,"
			  "user_id BIGINT NOT NULL,"
			  "refdb_id BIGINT NOT NULL,"
			  "xuser_reprint TEXT DEFAULT \'NOT IN FILE\',"
			  "xuser_date DATE,"
			  "xuser_avail TEXT,"
			  "xuser_notes TEXT)", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(220));
    return 220;
  }
   
  dbi_result_free(dbires);

  /* create t_xuser table indexes */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxuser_userid ON t_%sxuser (user_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(220));
    return 220;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxuser_refid ON t_%sxuser (refdb_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(220));
    return 220;
  }
   
  dbi_result_free(dbires);

  /* create the notes xtable */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxnote ("
			  "xnote_id INTEGER PRIMARY KEY,"
			  "note_id BIGINT NOT NULL,"
			  "xref_id BIGINT NOT NULL,"
			  "xnote_type TEXT DEFAULT \'REFERENCE\')", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(221));
    return 221;
  }
   
  dbi_result_free(dbires);

  /* create t_xnote table indexes */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxnote_noteid ON t_%sxnote (note_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(221));
    return 221;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxnote_xrefid ON t_%sxnote (xref_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(221));
    return 221;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxnote_xnotetype ON t_%sxnote (xnote_type)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(221));
    return 221;
  }
   
  dbi_result_free(dbires);

  /* create the links xtable */
  snprintf(sql_command, sql_command_len, "CREATE %sTABLE t_%sxlink ("
			  "xlink_id INTEGER PRIMARY KEY,"
			  "link_id BIGINT NOT NULL,"
			  "xref_id BIGINT NOT NULL,"
			  "user_id BIGINT DEFAULT 0,"
			  "xlink_type TEXT DEFAULT \'URL\',"
			  "xlink_source TEXT DEFAULT \'REFERENCE\')", temporary, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(258));
    return 258;
  }
   
  dbi_result_free(dbires);

  /* create t_xlink table indexes */
  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxlink_linkid ON t_%sxlink (link_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(258));
    return 258;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxlink_xrefid ON t_%sxlink (xref_id)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(258));
    return 258;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxlink_xlinktype ON t_%sxlink (xlink_type)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(258));
    return 258;
  }
   
  dbi_result_free(dbires);

  snprintf(sql_command, sql_command_len, "CREATE INDEX i_%sxlink_xlinksource ON t_%sxlink (xlink_source)", prefix, prefix);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);

  if (!dbires) {
    free(sql_command);
    LOG_PRINT(LOG_ERR, get_status_msg(258));
    return 258;
  }
   
  dbi_result_free(dbires);
  free(sql_command);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  create_xdup_tables_sqlite3(): creates SQLite3-specific duplicate check tables

  int create_xdup_tables_sqlite3 returns >0 if error, 0 if successful 

  dbi_conn conn database connection structure

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int create_xdup_tables_sqlite3(dbi_conn conn) {
  char* sql_command;
  size_t sql_command_len = 512;
  dbi_result dbires;

  /* get us some memory for the query strings */
  if ((sql_command = malloc(sql_command_len)) == NULL) {
    return 801;
  }
  sprintf(sql_command, "CREATE TEMPORARY TABLE t_temp_xdup ("
	  "xdup_id  INTEGER PRIMARY KEY,"
	  "xdup_type VARCHAR(10),"
	  "match_type VARCHAR(10),"
	  "temp_refdb_id BIGINT,"
	  "refdb_id BIGINT,"
	  "value_name TEXT,"
	  "temp_value_name TEXT,"
	  "value_name_2 TEXT,"
	  "temp_value_name_2 TEXT,"
	  "value_name_3 TEXT,"
	  "temp_value_name_3 TEXT)");
  
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);
  free(sql_command);

  if (!dbires) {
    LOG_PRINT(LOG_ERR, get_status_msg(268));
    return 268;
  }
   
  dbi_result_free(dbires);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  is_user_pgsql(): checks whether a user exists

  int is_user_pgsql returns 1 if the user exists, 0 if the user doesn't
  exist, -1 if an error occurs 

  dbi_conn conn connection to the database to query

  const char* username ptr to string with the name of the user

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int is_user_pgsql(dbi_conn conn, const char* username) {
  char sql_command[256];
  dbi_result dbires;

  /* The PostgreSQL system table pg_user contains a list of users */
  sprintf(sql_command, "SELECT usename FROM pg_user WHERE usename=\'%s\'", username);
  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);


  if (!dbires) {
    return -1;
  }
   
  if (dbi_result_get_numrows(dbires)) {
    dbi_result_free(dbires);
    return 1;
  }
  else {
    dbi_result_free(dbires);
    return 0;
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  is_group_pgsql(): checks whether a group exists

  int is_group_pgsql returns 1 if the group exists, 0 if the group doesn't
  exist, -1 if an error occurs 

  dbi_conn conn connection to the database to query

  const char* dbname ptr to string with the name of the database
  related to the group

  int is_readonly if 1, checks for a read-only group. If 0, checks
                  for a read/write group

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int is_group_pgsql(dbi_conn conn, const char* dbname, int is_readonly) {
  char sql_command[256];
  dbi_result dbires;

  /* The PostgreSQL system table pg_group contains a list of groups */
  if (!is_readonly) {
    sprintf(sql_command, "SELECT groname FROM pg_group WHERE groname=\'%suser\'", dbname);
  }
  else {
    sprintf(sql_command, "SELECT groname FROM pg_group WHERE groname=\'%sruser\'", dbname);
  }

  LOG_PRINT(LOG_DEBUG, sql_command);

  dbires = dbi_conn_query(conn, sql_command);


  if (!dbires) {
    return -1;
  }
   
  if (dbi_result_get_numrows(dbires)) {
    dbi_result_free(dbires);
    return 1;
  }
  else {
    dbi_result_free(dbires);
    return 0;
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  create_temporary_tables(): creates temporary tables for checking refs

  int create_temporary tables returns >0 if error, 0 if successful 

  dbi_conn conn database connection structure

  struct CLIENT_REQUEST* ptr_clrequest ptr to structure with client info

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int create_temporary_tables(dbi_conn conn, struct CLIENT_REQUEST* ptr_clrequest) {
  const char *drivername;

  drivername = dbi_driver_get_name(dbi_conn_get_driver(conn));

  if (!strcmp(drivername, "mysql")) {
    return create_tables_mysql(conn, ptr_clrequest, 1 /* temporary */);
  }
  else if (!strcmp(drivername, "pgsql")) {
    return create_tables_pgsql(conn, ptr_clrequest, 1 /* temporary */);
  }
  if (!strcmp(drivername, "sqlite")) {
    return create_tables_sqlite(conn, ptr_clrequest, 1 /* temporary */);
  }
  if (!strcmp(drivername, "sqlite3")) {
    return create_tables_sqlite3(conn, ptr_clrequest, 1 /* temporary */);
  }
  else {
    return 1;
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_conn_transaction(): deals with starting/ending transactions

  static int my_dbi_conn_transaction returns 0 if ok, 1 if error

  dbi_conn conn database connection

  int trans_type 0 is "BEGIN", 1 is "COMMIT", 2 is "ROLLBACK"

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int my_dbi_conn_transaction(dbi_conn conn, int trans_type) {
  const char *drivername;
  char sql_command[2][3][14] = {
    {"BEGIN WORK", "COMMIT WORK", "ROLLBACK WORK"},
    {"BEGIN", "COMMIT", "ROLLBACK"}
  };

  dbi_result dbires;

  drivername = dbi_driver_get_name(dbi_conn_get_driver(conn));

  if (!strcmp(drivername, "pgsql")) {
    dbires = dbi_conn_query(conn, sql_command[0][trans_type]);
    LOG_PRINT(LOG_DEBUG, sql_command[0][trans_type]);
    if (!dbires) {
      return 1;
    }
    dbi_result_free(dbires);
  }
  else  { /* SQLite and MySQL */
    /* MySQL supports transactions only in transaction-safe tables
       (InnoDB, BDB) but the transaction-related commands do not cause
       errors in the other table types. Therefore it safe to use them
       anyway, even if they don't have the desired effect. The
       non-transaction-safe tables work in auto-commit mode, i.e. each
       command is committed immediately. Rollbacks do not have any
       effect in this case */
    dbires = dbi_conn_query(conn, sql_command[1][trans_type]);
    LOG_PRINT(LOG_DEBUG, sql_command[1][trans_type]);
    if (!dbires) {
      return 1;
    }
    dbi_result_free(dbires);
  }

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_conn_begin(): begin a transaction

  int my_dbi_conn_begin returns 0 if ok, 1 if error

  dbi_conn conn database connection

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int my_dbi_conn_begin(dbi_conn conn) {
  return my_dbi_conn_transaction(conn, 0);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_conn_commit(): commits a transaction

  int my_dbi_conn_commit returns 0 if ok, 1 if error

  dbi_conn conn database connection

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int my_dbi_conn_commit(dbi_conn conn) {
  return my_dbi_conn_transaction(conn, 1);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_conn_rollback(): ends a transaction and discards all changes

  int my_dbi_conn_rollback returns 0 if ok, 1 if error

  dbi_conn conn database connection

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int my_dbi_conn_rollback(dbi_conn conn) {
  return my_dbi_conn_transaction(conn, 2);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_conn_lock(): locks reference database tables

  int my_dbi_conn_lock returns 0 if ok, 1 if error

  dbi_conn conn database connection

  int replace_ref indicates the context of the operation
                  0 = add data
		  1 = replace data
		  2 = update personal data
		  3 = add to temporary tables

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int my_dbi_conn_lock(dbi_conn conn, int replace_ref) {
  const char *drivername;
  char prefix[] = TEMP_TABLE_NAME_PREFIX;
  dbi_result dbires;

  /* fix insert */
  if (replace_ref != 3) {
    *prefix = '\0';
  }

  drivername = dbi_driver_get_name(dbi_conn_get_driver(conn));

  if (!strcmp(drivername, "mysql")) {
    char* sql_command;

    if ((sql_command = malloc(512)) == NULL) {
      return 1;
    }
    sprintf(sql_command, "LOCK TABLES t_%srefdb WRITE, t_%sauthor WRITE, t_%skeyword WRITE, t_%speriodical WRITE, t_%suser WRITE, t_%sxauthor WRITE, t_%sxkeyword WRITE, t_%sxuser WRITE, t_%snote WRITE, t_%sxnote WRITE, t_%slink WRITE, t_%sxlink WRITE, %s.t_journal_words WRITE", prefix, prefix, prefix, prefix, prefix, prefix, prefix, prefix, prefix, prefix, prefix, prefix, main_db);
    LOG_PRINT(LOG_DEBUG, sql_command);
    dbires = dbi_conn_query(conn, sql_command);
    free(sql_command);
    if (!dbires) {
      return 1;
    }
    dbi_result_free(dbires);
  }
  else if (!strcmp(drivername, "pgsql")) {
    int i;
    char sql_command[64];
    const char tables[12][13] = {
      "refdb",
      "author",
      "keyword",
      "periodical",
      "user",
      "xauthor",
      "xkeyword",
      "xuser",
      "note",
      "xnote",
      "link",
      "xlink"
    };

    for (i = 0; i < 12; i++) {
      sprintf(sql_command, "LOCK TABLE t_%s%s IN SHARE MODE", prefix, tables[i]);
      dbires = dbi_conn_query(conn, sql_command);
      LOG_PRINT(LOG_DEBUG, sql_command);
      if (!dbires) {
	return 1;
      }
      dbi_result_free(dbires);
    }
  }
  /* else: sqlite does not support table locking */

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_conn_lock_note(): locks notes database tables

  int my_dbi_conn_lock_note returns 0 if ok, 1 if error

  dbi_conn conn database connection

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int my_dbi_conn_lock_note(dbi_conn conn) {
  const char *drivername;
  dbi_result dbires;

  drivername = dbi_driver_get_name(dbi_conn_get_driver(conn));

  if (!strcmp(drivername, "mysql")) {
    char* sql_command;

    if ((sql_command = malloc(256)) == NULL) {
      return 1;
    }
    strcpy(sql_command, "LOCK TABLES t_keyword WRITE, t_user WRITE, t_xkeyword WRITE, t_xuser WRITE, t_note WRITE, t_xnote WRITE, t_periodical WRITE, t_author WRITE, t_refdb WRITE, t_link WRITE, t_xlink WRITE");
    LOG_PRINT(LOG_DEBUG, sql_command);
    dbires = dbi_conn_query(conn, sql_command);
    free(sql_command);
    if (!dbires) {
      return 1;
    }
    dbi_result_free(dbires);
  }
  else if (!strcmp(drivername, "pgsql")) {
    int i;
    char sql_command[64];
    const char tables[8][13] = {
      "t_keyword",
      "t_user",
      "t_xkeyword",
      "t_xuser",
      "t_note",
      "t_xnote",
      "t_link",
      "t_xlink"
    };

    for (i = 0; i < 8; i++) {
      sprintf(sql_command, "LOCK TABLE %s IN SHARE MODE", tables[i]);
      dbires = dbi_conn_query(conn, sql_command);
      LOG_PRINT(LOG_DEBUG, sql_command);
      if (!dbires) {
	return 1;
      }
      dbi_result_free(dbires);
    }
  }
  /* else: sqlite does not support table locking */

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_conn_unlock(): unlocks locked tables

  int my_dbi_conn_unlock returns 0 if ok, 1 if error

  dbi_conn conn database connection

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int my_dbi_conn_unlock(dbi_conn conn) {
  dbi_result dbires;
  const char *drivername;

  drivername = dbi_driver_get_name(dbi_conn_get_driver(conn));

  if (!strcmp(drivername, "mysql")) {
    dbires = dbi_conn_query(conn, "UNLOCK TABLES");
    LOG_PRINT(LOG_DEBUG, "UNLOCK TABLES");
    if (!dbires) {
      return 1;
    }
    dbi_result_free(dbires);
  }
  /* else: pgsql unlocks when a transaction is finished, */
  /*       sqlite does not support lock/unlock */

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_conn_get_cap(): returns a driver capability

  const char* my_dbi_conn_get_cap returns a capability string or NULL on error

  dbi_conn conn database connection

  const char* cap string containing the name of the requested
  capability

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
const char* my_dbi_conn_get_cap(dbi_conn conn, const char* cap) {
  dbi_driver driver;
  const char *drivername;

  driver = dbi_conn_get_driver(conn);

  drivername = dbi_driver_get_name(driver);

  /* check whether we already have the version-specific information */
  if (!ptr_dbcaps->has_versioninfo) {
    set_cap_versioninfo(conn, drivername);
  }

  return my_dbi_driver_get_cap(driver, cap);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_driver_get_cap(): returns a driver capability

  const char* my_dbi_driver_get_cap returns a capability string or NULL on error

  dbi_driver database driver

  const char* cap string containing the name of the requested
  capability

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
const char* my_dbi_driver_get_cap(dbi_driver driver, const char* cap) {
  if (!strcmp(cap, "multiple_db")) {
    return ptr_dbcaps->multiple_db;
  }
  else if (!strcmp(cap, "enum")) {
    return ptr_dbcaps->sql_enum;
  }
  else if (!strcmp(cap, "rlike")) {
    return ptr_dbcaps->rlike;
  }
  else if (!strcmp(cap, "not_rlike")) {
    return ptr_dbcaps->not_rlike;
  }
  else if (!strcmp(cap, "transaction")) {
    return ptr_dbcaps->transaction;
  }
  else if (!strcmp(cap, "localhost")) {
    return ptr_dbcaps->localhost;
  }
  else if (!strcmp(cap, "encoding")) {
    return ptr_dbcaps->encoding;
  }
  else if (!strcmp(cap, "groups")) {
    return ptr_dbcaps->groups;
  }
  else if (!strcmp(cap, "admin_systable")) {
    return ptr_dbcaps->admin_systable;
  }
  else if (!strcmp(cap, "listall")) {
    return ptr_dbcaps->listall;
  }
  else if (!strcmp(cap, "bigint")) {
    return ptr_dbcaps->bigint;
  }
  else if (!strcmp(cap, "union")) {
    return ptr_dbcaps->sql_union;
  }
  else if (!strcmp(cap, "named_seq")) {
    return ptr_dbcaps->named_seq;
  }
  else if (!strcmp(cap, "charlength")) {
    return ptr_dbcaps->charlength;
  }
  else if (!strcmp(cap, "substring")) {
    return ptr_dbcaps->substring;
  }
  else if (!strcmp(cap, "substring_from")) {
    return ptr_dbcaps->substring_from;
  }
  else if (!strcmp(cap, "substring_for")) {
    return ptr_dbcaps->substring_for;
  }
  else if (!strcmp(cap, "except")) {
    return ptr_dbcaps->sql_except;
  }
  else {
    return ptr_dbcaps->defval;
  }

  /* unknown cap */
  return NULL;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_result_get_idval(): returns an id value by field name. Takes
  care of the fact that some db servers do
  not support ulonglongs

  unsigned long long my_dbi_result_get_idval returns an id value 

  dbi_result dbires result of a previous query

  const char* fieldname name of the field to retrieve

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
unsigned long long my_dbi_result_get_idval(dbi_result dbires, const char* fieldname) {
  dbi_conn conn;
  unsigned int the_uint;
  unsigned long long the_ulonglong;

  conn = dbi_result_get_conn(dbires);

  if (!strcmp(my_dbi_conn_get_cap(conn, "bigint"), "f")) {
    /* database server does not support long long ints, retrieve as 
       long int instead and cast */
    the_uint = dbi_result_get_uint(dbires, fieldname);
    /*     sprintf(buffer, "ulong: u %u<< lu %lu<< llu %llu", the_ulong, the_ulong, the_ulong); */
    /*     LOG_PRINT(LOG_DEBUG, buffer); */
    return (unsigned long long)the_uint;
  }
  else {
    the_ulonglong = dbi_result_get_ulonglong(dbires, fieldname);
    /*     sprintf(buffer, "ulonglong: u %u<< lu %lu<< llu %llu", the_ulonglong, the_ulonglong, the_ulonglong); */
    /*     LOG_PRINT(LOG_DEBUG, buffer); */
    return the_ulonglong;
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_result_get_idval_idx(): returns an id value by index. Takes
  care of the fact that some db servers
  do not support ulonglongs

  unsigned long long my_dbi_result_get_idval_idx returns an id value 

  dbi_result dbires result of a previous query

  unsigned int idx index of field to query

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
unsigned long long my_dbi_result_get_idval_idx(dbi_result dbires, unsigned int idx) {
  dbi_conn conn;

  conn = dbi_result_get_conn(dbires);

  if (!strcmp(my_dbi_conn_get_cap(conn, "bigint"), "f")) {
    /* database server does not support long long ints, retrieve as 
       long int instead and cast */
    return (unsigned long long)dbi_result_get_uint_idx(dbires, idx);
  }
  else {
    return dbi_result_get_ulonglong_idx(dbires, idx);
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_result_get_int_idval(): returns an id value (4-byte integer)
                                 by field name.

  unsigned int my_dbi_result_get_int_idval returns an id value 

  dbi_result dbires result of a previous query

  const char* fieldname name of the field to retrieve

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
unsigned int my_dbi_result_get_int_idval(dbi_result dbires, const char* fieldname) {
  dbi_conn conn;
  unsigned int field_attrib;
  unsigned int the_uint;
  unsigned long long the_ulonglong;

  conn = dbi_result_get_conn(dbires);

  field_attrib = dbi_result_get_field_attrib(dbires, fieldname, DBI_INTEGER_SIZE4, DBI_INTEGER_SIZE4);
  /*   printf("field_type went to %d\n", (int)field_type); */

  if (field_attrib) {
    /* value is a 4-byte integer */
    the_uint = dbi_result_get_uint(dbires, fieldname);
    /*     sprintf(buffer, "ulong: u %u<< lu %lu<< llu %llu", the_ulong, the_ulong, the_ulong); */
    /*     LOG_PRINT(LOG_DEBUG, buffer); */
    return the_uint;
  }
  else {
    /* value is an 8-byte integer. Cast to uint */
    the_ulonglong = dbi_result_get_ulonglong(dbires, fieldname);
    /*     sprintf(buffer, "ulonglong: u %u<< lu %lu<< llu %llu", the_ulonglong, the_ulonglong, the_ulonglong); */
    /*     LOG_PRINT(LOG_DEBUG, buffer); */
    return (unsigned int)the_ulonglong;
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_result_get_int_idval_idx(): returns an id value (4-byte integer)
                                     by index.

  unsigned int my_dbi_result_get_int_idval_idx returns an id value 

  dbi_result dbires result of a previous query

  unsigned int idx index of the field to retrieve

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
unsigned int my_dbi_result_get_int_idval_idx(dbi_result dbires, unsigned int idx) {
  dbi_conn conn;
  unsigned int field_attrib;
  unsigned int the_uint;
  unsigned long long the_ulonglong;

  conn = dbi_result_get_conn(dbires);

  field_attrib = dbi_result_get_field_attrib_idx(dbires, idx, DBI_INTEGER_SIZE4, DBI_INTEGER_SIZE4);
  /*   printf("field_type went to %d\n", (int)field_type); */

  if (field_attrib) {
    /* value is a 4-byte integer */
    the_uint = dbi_result_get_uint_idx(dbires, idx);
    /*     sprintf(buffer, "ulong: u %u<< lu %lu<< llu %llu", the_ulong, the_ulong, the_ulong); */
    /*     LOG_PRINT(LOG_DEBUG, buffer); */
    return the_uint;
  }
  else {
    /* value is an 8-byte integer. Cast to uint */
    the_ulonglong = dbi_result_get_ulonglong_idx(dbires, idx);
    /*     sprintf(buffer, "ulonglong: u %u<< lu %lu<< llu %llu", the_ulonglong, the_ulonglong, the_ulonglong); */
    /*     LOG_PRINT(LOG_DEBUG, buffer); */
    return (unsigned int)the_ulonglong;
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_conn_get_versioninfo(): retrieves the version info of a db engine

  int my_dbi_conn_get_versioninfo returns 0 if ok, 1 if error

  dbi_conn conn connection to a database engine

  struct VERSIONINFO* ptr_ver ptr to  struct to receive version info

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int my_dbi_conn_get_versioninfo(dbi_conn conn, struct VERSIONINFO* ptr_ver) {
  char* versioninfo = NULL;
  char sql_command[] = "SELECT VERSION()";
  dbi_result dbires;

  ptr_ver->major = 0;
  ptr_ver->minor = 0;
  ptr_ver->minuscule = 0;

  dbires = dbi_conn_query(conn, sql_command);
  LOG_PRINT(LOG_DEBUG, sql_command);
  if (dbires) {
    if (dbi_result_next_row(dbires)) {
      versioninfo = dbi_result_get_string_copy_idx(dbires, 1); /* 1-base index */
    }
    dbi_result_free(dbires);
  }

  if (versioninfo) {
    if (parse_versioninfo(versioninfo, ptr_ver)) {
      return 1;
    }
    free(versioninfo);
    /* 	printf("major: %d minor: %d minuscule: %d\n", ver.major, ver.minor, ver.minuscule); */
  }

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  set_cap_versioninfo(): sets version-specific info in dbcaps structure

  static int set_cap_versioninfo returns 0 if ok, 1 if error

  dbi_conn conn connection to a database engine

  const char* drivername name of db driver

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int set_cap_versioninfo(dbi_conn conn, const char* drivername) {
  struct VERSIONINFO ver;

  /* try only once */
  ptr_dbcaps->has_versioninfo = 1;

  if (my_dbi_conn_get_versioninfo(conn, &ver)) {
    return 1;
  }

  if (!strcmp(drivername, "mysql")) {
    if (ver.major >= 4) { /* 4.0 and later */
      strcpy(ptr_dbcaps->sql_union, "t");
    }
    if ((ver.major == 4 && ver.minor > 0)
	|| ver.major > 4) { /* 4.1 and later */
      strcpy(ptr_dbcaps->encoding, "CHARACTER SET");
    }
  }
  /* else: nothing to do */

  return 0;
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_initialize(): wrapper for libdbi 1.0 vs. pre-1.0 migration

  int my_dbi_initialize returns the number of drivers loaded, or -1
                        in case of an error

  const char *driverdir the path of the directory that contains the
                        drivers, or NULL to use the compile-time
			default

  dbi_inst *pInst pointer to instance handle. Will be updated if the
                        function is successful

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int my_dbi_initialize(const char *driverdir, dbi_inst *pInst) {
#if !defined (LIBDBI_LIB_CURRENT) || LIBDBI_LIB_CURRENT < 2
  return dbi_initialize(driverdir);
#else
  return dbi_initialize_r(driverdir, pInst);
#endif
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_shutdown(): wrapper for libdbi 1.0 vs. pre-1.0 migration

  void my_dbi_shutdown

  dbi_inst Inst instance handle

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void my_dbi_shutdown(dbi_inst Inst) {
#if !defined (LIBDBI_LIB_CURRENT) || LIBDBI_LIB_CURRENT < 2
  dbi_shutdown();
#else
  dbi_shutdown_r(Inst);
#endif
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_driver_list(): wrapper for libdbi 1.0 vs. pre-1.0 migration

  dbi_driver my_dbi_driver_list returns the next driver in the driver
                        list, or NULL if no more are available

  dbi_driver Current the driver last retrieved with this function, or
                        NULL to retrieve the first driver

  dbi_inst Inst instance handle

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
dbi_driver my_dbi_driver_list(dbi_driver Current, dbi_inst Inst) {
#if !defined (LIBDBI_LIB_CURRENT) || LIBDBI_LIB_CURRENT < 2
  return dbi_driver_list(Current);
#else
  return dbi_driver_list_r(Current, Inst);
#endif
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_conn_new(): wrapper for libdbi 1.0 vs. pre-1.0 migration

  dbi_conn my_dbi_conn_new returns a connection instance if successful,
                     or NULL in case of an error

  const char *name name of the driver to use

  dbi_inst Inst instance handle

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
dbi_conn my_dbi_conn_new(const char *name, dbi_inst Inst) {
#if !defined (LIBDBI_LIB_CURRENT) || LIBDBI_LIB_CURRENT < 2
  return dbi_conn_new(name);
#else
  return dbi_conn_new_r(name, Inst);
#endif
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  my_dbi_conn_error_flag(): wrapper for libdbi 1.0 vs. pre-1.0 migration

  int my_dbi_conn_error_flag returns 0 if there was no error
                     or a nonzero error code if the last operation
		     of the connection failed

  dbi_conn Conn the connection

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int my_dbi_conn_error_flag(dbi_conn Conn) {
#if !defined (LIBDBI_LIB_CURRENT) || LIBDBI_LIB_CURRENT < 2
  return dbi_conn_error_flag(Conn);
#else
  return dbi_conn_error(Conn, NULL);
#endif
}

