function a_db = tests_db(a_sql_portal, query_string, query_id, props)

% tests_db - Create a tests_db object from the results of a SQL query.
%
% Usage:
% a_db = tests_db(a_sql_portal, query_string, query_id, props)
%
% Description:
%   Converter function to get a tests_db object properly annotated with
% the metadata obtained from the results of the executed SQL
% query. Currently this function is limited to importing numeric data only.
%
%   Parameters:
%	a_sql_portal: A sql_portal object.
%	query_string: An SQL query returning numeric results.
%	query_id: Identifier associated witht the query, to be passed
%		  into the tests_db object.
%	props: A structure with any optional properties passed to tests_db.
%		
%   Returns:
%	a_db: A tests_db object.
%
% See also: tests_db, database
%
% $Id$
%
% Author: Cengiz Gunay <cgunay@emory.edu>, 2007/11/29

% Copyright (c) 2007 Cengiz Gunay <cengique@users.sf.net>.
% This work is licensed under the Academic Free License ("AFL")
% v. 3.0. To view a copy of this license, please look at the COPYING
% file distributed with this software or visit
% http://opensource.org/licenses/afl-3.0.php.

% verbose warnings
vs = warning('query', 'verbose');
verbose = strcmp(vs.state, 'on');

if ~exist('props', 'var')
  props = struct;
end

if ~exist('query_id', 'var')
  query_id = [ 'Query ' query_string ' from ' get(a_sql_portal, 'id') ];
end

% only numeric data
setdbprefs('DataReturnFormat','numeric');

% run query
if verbose
  disp(['SQL query: "' query_string '"']);
end

conn = a_sql_portal.db_conn.Handle;
use_java_direct = false;

if use_java_direct
  % use java directly
  stmt = ...
      conn.createStatement(java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, ...
                           java.sql.ResultSet.CONCUR_READ_ONLY);
  stmt.setFetchSize(10);                  % test/debug
                                          % stmt.setMaxRows(10);
                                          %% test/debug
  stmt.setQueryTimeout(24 * 3600);        % 24 hours
  rs = stmt.executeQuery(query_string);
  
  % go to last row
  if last(rs) ~= true 
    error('Cannot move cursor to end of query result set.');
  end

  num_rows = getRow(rs);
  rsm = getMetaData(rs);
  num_cols = getColumnCount(rsm);

  if verbose
    num_cols, num_rows
  end

  % go back to beginning
  beforeFirst(rs);

  % allocate space for incoming data
  new_data = repmat(NaN, num_rows, num_cols);

  % read in parts
  max_read = 256 * 1024 * 1024; % bytes
  max_rows = floor(max_read / 8 / num_cols); % for double precision

  if verbose
    max_rows
  end

else
  a_cursor = cursor(a_sql_portal.db_conn, query_string);

  if strfind(a_cursor.Message, 'java.io.EOFException')
    error([ a_cursor.Message sprintf('\n') ...
            'Error: Database connection timed out. Please reconnnect.']);
  elseif strfind(lower(a_cursor.Message), 'error')
    disp(['SQL Query: "' query_string '"']);
    a_cursor
    error(['Error in SQL query:' sprintf('\n') a_cursor.Message]);
  end

  if ~ isempty(a_cursor.Message), disp(a_cursor.Message), end
  
  if verbose
    a_cursor
  end

end


if use_java_direct
  % TODO: go back to original! this is ten times slower!
  % need to write native java code to make it faster
  
  % read all rows in resultset
  row_num = 1;
  while next(rs)
    for col_num = 1:num_cols
      new_data(row_num, col_num) = rs.getDouble(col_num);
    end
    row_num = row_num + 1;
  end

  col_names = {};
  rsm = getMetaData(rs);
  for col_num = 1:num_cols
    col_names{col_num} = char(rsm.getColumnName(col_num));
  end

  % clean up database objects
  rs.close();
  stmt.close();

else

  % analyze the output
  % get the Java object directly, Database Toolbox sucks
  %rs = a_cursor.ResultSet;
  
  % how to get num_rows before executing query??
  
  % do in a single batch for now
  % get the data
  a_cursor = fetch(a_cursor);
  new_data = a_cursor.Data;
  
% $$$   for batch_num = 1:ceil(num_rows / max_rows)
% $$$     % get the data
% $$$     a_cursor = fetch(a_cursor, max_rows);
% $$$     
% $$$     start_row = (batch_num - 1) * max_rows + 1;
% $$$     if verbose
% $$$       start_row
% $$$     end
% $$$     new_data(start_row:(start_row + min(max_rows, size(a_cursor.Data, 1)) - 1), :) = ...
% $$$         a_cursor.Data;
% $$$   end

  % get names (only after fetching)
  attrs = attr(a_cursor);
  col_names = { attrs.fieldName };

  % free resources. important! otherwise java runs out of memory very quickly.
  close(a_cursor);
end 

if isempty(new_data) || ~isnumeric(new_data)
  warning(['SQL query returned no results: ' sprintf('\n') '''' query_string ...
           '''']);
  % reset to empty matrix in case it's the 'No Data' string
  new_data = [];                        
end

% create the tests_db object
a_db = tests_db(new_data, col_names, {}, query_id, props);