query_handler.cpp

Go to the documentation of this file.
00001 /*****************************************************************************\
00002 *                                                                             *
00003 *  Name   : query_handler                                                     *
00004 *  Author : Chris Koeritz                                                     *
00005 *                                                                             *
00006 *******************************************************************************
00007 * Copyright (c) 2008-$now By Author.  This program is free software; you can  *
00008 * redistribute it and/or modify it under the terms of the GNU General Public  *
00009 * License as published by the Free Software Foundation; either version 2 of   *
00010 * the License or (at your option) any later version.  This is online at:      *
00011 *     http://www.fsf.org/copyleft/gpl.html                                    *
00012 * Please send any updates to: fred@gruntose.com                               *
00013 \*****************************************************************************/
00014 
00015 #include "common.h"
00016 #include "query_handler.h"
00017 #include "login_info.h"
00018 
00019 #include <basis/function.h>
00020 #include <basis/log_base.h>
00021 #include <basis/portable.h>
00022 #include <basis/set.cpp>
00023 #include <basis/string_array.h>
00024 #include <data_struct/deep_array.cpp>
00025 #include <opsystem/byte_filer.h>
00026 #include <opsystem/directory.h>
00027 #include <opsystem/filename.h>
00028 #include <opsystem/ini_config.h>
00029 #include <textual/list_parsing.h>
00030 
00031 #include <malloc.h>
00032 
00033 #define BASE_LOG(s) EMERGENCY_LOG(program_wide_logger(), s)
00034 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger(), filename(portable::application_name()).basename().raw() + " " + s)
00035 
00037 
00038 class deep_string_array : public deep_array<istring> {};
00039 
00041 
00043 
00044 query_handler::query_handler()
00045 : _global_current_db(NIL),
00046   _bind_strings(new deep_string_array),
00047   _bind_ints(new int_array)
00048 {
00049   common_database_support::_error_was_seen = false;
00050 }
00051 
00052 query_handler::~query_handler()
00053 {
00054   FUNCDEF("destructor");
00055   if (_global_current_db) {
00056     LOG(isprintf("closing database connection."));
00057     dbclose(_global_current_db);
00058     _global_current_db = NIL;
00059   }
00060   dbexit();
00061   WHACK(_bind_strings);
00062   WHACK(_bind_ints);
00063 }
00064 
00065 istring query_handler::get_bound_string(int index)
00066 { return _bind_strings->get(index); }
00067 
00068 int query_handler::get_bound_int(int index)
00069 { return _bind_ints->get(index); }
00070 
00071 bool query_handler::prepare_for_query(const istring &config_file,
00072     database_login_info &persistent_login_info)
00073 {
00074   FUNCDEF("prepare_for_query");
00075   ini_configurator ini(config_file);
00076 
00077   database_login_info temp_info(persistent_login_info.db_section);
00078   bool worked = database_login_info::read_login_info(ini, temp_info);
00079   if (!worked) {
00080     LOG(istring("ERROR: failed to read db login info from ") + config_file);
00081     return false;
00082   }
00083 
00084   // if the login info is different, then close the old database and open
00085   // the new one with the parameters.
00086   bool retried_connection = false;
00087   if (!_global_current_db || !temp_info.identical(persistent_login_info)
00088       || common_database_support::_error_was_seen) {
00089     persistent_login_info = temp_info;
00090     retried_connection = true;
00091 
00092     if (common_database_support::_error_was_seen) {
00093       // if we saw an error previously, that's why we're taking the db down.
00094       LOG("handling previous db issue by re-opening database...");
00095       common_database_support::_error_was_seen = false;
00096     }
00097 
00098     persistent_login_info.update_freetds_configuration();
00099       // make sure the database knows about our parameter.
00100 
00101     bool worked = login_to_database(persistent_login_info);
00102     if (!worked) {
00103       LOG(istring("ERROR: failed to login to database ")
00104           + persistent_login_info.database
00105           + " using authentication info.");
00106       return false;
00107     }
00108 
00109     bool using_okay = use_database(persistent_login_info);
00110     if (!using_okay) {
00111       LOG(istring("ERROR: failed to USE database ")
00112           + persistent_login_info.database + " on server.");
00113       return false;
00114     }
00115   }
00116 
00117   if (!_global_current_db) {
00118     // we seem to have a serious problem here.  stop processing now.
00119     LOG("ERROR: failed to end up with a working database object");
00120     return false;
00121   } else {
00122     if (retried_connection) {
00123       LOG(istring("Now connected to database ")
00124           + persistent_login_info.database);
00125     }
00126   }
00127 
00128   return true;
00129 }
00130 
00131 bool query_handler::bind_columns()
00132 {
00133   FUNCDEF("bind_columns");
00134   if (!_global_current_db) { LOG("ERROR: null database object"); return false; }
00135 
00136   bool to_return = true;  // hopeful at first.
00137 
00138   int db_cols = dbnumcols(_global_current_db);
00140   for (int i = 1; i <= db_cols; i++) {
00141     int type = dbcoltype(_global_current_db, i);
00142     switch (type) {
00143       case SYBINT1:
00144       case SYBINT2:
00145       case SYBINT4: {
00146         if (_bind_ints->length() < db_cols + 1) {
00147           _bind_ints->insert(_bind_ints->length(),
00148               db_cols + 2 - _bind_ints->length());
00149         }
00150         if (dbbind(_global_current_db, i, INTBIND, sizeof(int),
00151             (BYTE *) &_bind_ints->use(i)) != SUCCEED) {
00152           to_return = false;
00153           LOG(isprintf("ERROR: had problem with binding to int on column %d", i));
00154         }
00155         break;
00156       }
00157       case SYBCHAR:
00158       case SYBVARCHAR:
00159       case SYBTEXT: {
00160         if (_bind_strings->length() < db_cols + 1) {
00161           _bind_strings->insert(_bind_strings->length(),
00162               db_cols + 2 - _bind_strings->length());
00163         }
00164         if (_bind_strings->get(i).length() < MAX_DB_STRING + 1) {
00165           _bind_strings->put(i, istring('\0', MAX_DB_STRING + 1));
00166         }
00167         char *field = _bind_strings->use(i).access();
00168         if (SUCCEED != dbbind(_global_current_db, i, STRINGBIND, -1, (BYTE *)field)) {
00169 //STRINGBIND
00170 //VARYCHARBIND
00171           to_return = false;
00172           LOG(isprintf("ERROR: had problem with binding to string on column %d", i));
00173         }
00174         break;
00175       }
00176       default: {
00177         // we don't consider this case an error; we simply haven't implemented
00178         // the type handling yet.
00179         LOG(isprintf("unhandled type for column %d, type is %d", i, type));
00180         break;
00181       }
00182     }
00183   }
00184   return to_return;
00185 }
00186 
00187 bool query_handler::print_row(log_base &output)
00188 {
00189   FUNCDEF("print_row");
00190   if (!_global_current_db) { LOG("ERROR: null database object"); return false; }
00191   bool to_return = true;
00192   istring to_show;
00193   for (int i = 1; i <= dbnumcols(_global_current_db); i++) {
00194     if (i != 1) to_show += " ";  // separator.
00195     int type = dbcoltype(_global_current_db, i);
00196     switch (type) {
00197       case SYBINT1:
00198       case SYBINT2:
00199       case SYBINT4: {
00200         to_show += isprintf("%d", _bind_ints->get(i));
00201         break;
00202       }
00203       case SYBCHAR:
00204       case SYBVARCHAR:
00205       case SYBTEXT: {
00206 //STRINGBIND
00207 //VARYCHARBIND
00208         istring found = (*_bind_strings)[i];
00209         found.shrink();  // get proper length.
00210         to_show += found;
00211         break;
00212       }
00213       default: {
00214         to_show += "UNHANDLED";
00215         break;
00216       }
00217     }
00218   }
00219   output.log(to_show);
00220   if (&output != &program_wide_logger()) {
00221     BASE_LOG(to_show);
00222   }
00223 }
00224 
00225 bool query_handler::show_this_row(int row_num, log_base &output)
00226 {
00227   FUNCDEF("show_this_row");
00228   if (!_global_current_db) { LOG("ERROR: null database object"); return false; }
00229   bool to_return = true;
00230   istring to_show;
00231   for (int i = 1; i <= dbnumcols(_global_current_db); i++) {
00232     if (i != 1) to_show += "\t";  // separator.
00233     int type = dbcoltype(_global_current_db, i);
00234     switch (type) {
00235       case SYBINT1:
00236       case SYBINT2:
00237       case SYBINT4: {
00238         to_show += isprintf("I=%d", _bind_ints->get(i));
00239         break;
00240       }
00241       case SYBCHAR:
00242       case SYBVARCHAR:
00243       case SYBTEXT: {
00244 //STRINGBIND
00245 //VARYCHARBIND
00246         to_show += "S=";
00247         istring found = (*_bind_strings)[i];
00248         found.shrink();  // get proper length.
00249         to_show += found;
00250         break;
00251       }
00252       default: {
00253         to_show += "UNHANDLED";
00254         break;
00255       }
00256     }
00257   }
00258   output.log(isprintf("#%d: ", row_num) + to_show);
00259   if (&output != &program_wide_logger()) {
00260     LOG(isprintf("#%d: ", row_num) + to_show);
00261   }
00262 }
00263 
00264 void query_handler::show_column_structure()
00265 {
00266   FUNCDEF("show_column_structure");
00267   if (!_global_current_db) { LOG("ERROR: null database object"); return; }
00268   for (int i = 1; i <= dbnumcols(_global_current_db); i++) {
00269     LOG(isprintf("   col %d: type=%d name=%s", i, dbcoltype(_global_current_db, i),
00270         dbcolname(_global_current_db, i)));
00271   }
00272 }
00273 
00274 void query_handler::show_results(bool verbose, log_base &output)
00275 {
00276   FUNCDEF("show_results");
00277   if (!_global_current_db) { LOG("ERROR: null database object"); return; }
00278   int db_result = REG_ROW;
00279   // bail out when the result comes back other than success.
00280   int row_num = 1;
00281   BASE_LOG(istring('-', 42));
00282   bind_columns();
00283   while (db_result == REG_ROW) {
00284     // see whether there's a next row.
00285     db_result = dbnextrow(_global_current_db);
00286     if (db_result == REG_ROW) {
00287       if (verbose)
00288         show_this_row(row_num++, output);
00289       else
00290         print_row(output);
00291     } else if (db_result != NO_MORE_ROWS) {
00292       // report that an unexpected error occurred.      
00293       LOG(isprintf("ERROR: failure to get next row=%d", db_result));
00294     }
00295   }
00296   BASE_LOG(istring('-', 42));
00297 }
00298 
00299 bool query_handler::login_to_database(const database_login_info &cred)
00300 {
00301   FUNCDEF("login_to_database");
00302   LOG(isprintf("about to logon as \"%s\"", cred.user_name.s()));
00303 
00304   // close any current database we might be looking at.
00305   if (_global_current_db) {
00306 //      LOG(isprintf("closing previous database connection."));
00307     dbclose(_global_current_db);
00308     _global_current_db = NIL;
00309     dbexit();
00310   }
00311 
00312   dbinit();
00313   dberrhandle(common_database_support::syb_err_handler);
00314   dbmsghandle(common_database_support::syb_msg_handler);
00315 
00316   LOGINREC *database_login = dblogin();
00317   DBSETLPWD(database_login, cred.password.s());
00318   DBSETLUSER(database_login, cred.user_name.s());
00319   DBSETLAPP(database_login, portable::application_name().s());
00320 
00321 //make note of port if we're using that instead.
00322   LOG(isprintf("about to connect to \"%s\" on instance %s",
00323       cred.db_host.s(), cred.db_instance.s()));
00324 
00325 //make note of port if we're using that instead.
00326   _global_current_db = dbopen(database_login, cred.db_section.s());
00327   if (!_global_current_db) {
00328     LOG(isprintf("ERROR: unable to connect to \"%s\" on instance %s",
00329         cred.db_host.s(), cred.db_instance.s()));
00330     dbloginfree(database_login);  
00331     return NIL;
00332   }
00333   dbloginfree(database_login);  
00334   return !!_global_current_db;
00335 }
00336 
00337 bool query_handler::use_database(const database_login_info &cred)
00338 {
00339   FUNCDEF("use_database");
00340   if (!_global_current_db) { LOG("ERROR: null database object"); return false; }
00341   LOG(isprintf("using database \"%s\"", cred.database.s()));
00342   if (cred.database.length()) {
00343     DBINT erc = dbuse(_global_current_db, cred.database.s());
00344     if (erc != SUCCEED) {
00345       LOG(isprintf("ERROR: unable to use database %s", cred.database.s()));
00346       return false;
00347     }
00348   }
00349   return true;
00350 }
00351 
00352 bool query_handler::process_query(const istring &query, log_base &output)
00353 {
00354   FUNCDEF("process_query");
00355   if (!_global_current_db) { LOG("ERROR: null database object"); return false; }
00356   LOG(istring("process_query on: ") + query);
00357   dbfreebuf(_global_current_db);
00358   dbcmd(_global_current_db, query.s());
00359   dbsqlexec(_global_current_db);
00360 
00361   if (dbresults(_global_current_db) != SUCCEED) {
00362     LOG(isprintf("ERROR: was expecting a result set and got none."));
00364     return false;
00365   }
00366   show_results(false, output);
00367   dbcanquery(_global_current_db);
00368   return true;
00369 }
00370 
00372 
00373 /*
00374 ...grist for the mill of getting metadata queries done.
00375 
00376 //this needs to become the "show me the tables" function.
00377 
00378   istring sql_query;  // the actual query string we'll use.
00379   istring query_limiter;  // additions on the end of the query.
00380 
00382 
00383 // make this into a retrieve table names method.  probably the table name
00384 // below is definitive for MS SQL.
00385   LOG("\n\n");
00386 
00387   table_name = "INFORMATION_SCHEMA.TABLES";
00388 LOG(istring("selecting data from table ") + table_name);
00389 
00390   query_limiter = " where TABLE_NAME not like 'sys%' "
00391       "and TABLE_NAME not like 'tsvw_%' "
00392       "and TABLE_NAME not like 'MS%' "
00393       "and TABLE_NAME not like 'conflict_%' "
00394       "and TABLE_NAME not like 'ctsv_%' ";
00395 
00396 //different query below; need to capture which columns in a query builder.
00397   sql_query = "select TABLE_NAME from ";
00398   sql_query += table_name;
00399   sql_query += query_limiter;
00400   dbfreebuf(current_db);
00401   dbcmd(_global_current_db, sql_query.s());
00402   dbsqlexec(_global_current_db);
00403 
00404   if (dbresults(_global_current_db) != SUCCEED) {
00405     LOG(isprintf("ERROR: failure--was expecting a result set."));
00406     return 1;
00407   }
00408 
00409   show_column_structure(_global_current_db);
00410 
00411   show_results(current_db);
00412 
00414 
00415 //this is a grab column structure method.  make the table name a parameter.
00416 
00417   LOG("\n\n");
00418 
00419   table_name = "LL_SavedExpressions";
00420 LOG(istring("selecting data from table ") + table_name);
00421   query_limiter = "";
00422   sql_query = "select top 1 * from ";
00423 //special query with "top 1" for first item.
00424   sql_query += table_name;
00425   sql_query += query_limiter;
00426   dbfreebuf(current_db);
00427   dbcmd(_global_current_db, sql_query.s());
00428   dbsqlexec(_global_current_db);
00429 
00430   if (dbresults(_global_current_db) != SUCCEED) {
00431     LOG(isprintf("ERROR: failure--was expecting a result set."));
00432     return 1;
00433   }
00434 
00435   show_column_structure(_global_current_db);
00436 
00437   show_results(_global_current_db);
00438 
00439 */
00440 

Generated on Fri Nov 21 04:29:42 2008 for HOOPLE Libraries by  doxygen 1.5.1