function rs = pg_fetch_struct(conn, sql, varargin)
%PG_FETCH_STRUCT  Executes a SQL query and imports database data into matlab
%
%   Executes a SQL query with the (i) licensed Mathworks database
%   toolbox or otherwise with (ii) the JDBC driver directly
%   fetches the result and checks the result for
%   errors. Returns the resulting data in a struct with columns names as fields.
%   Doubles and integers are returned as numbers,
%   timestamps as DateNumbers and others as cell array of strings.
%
%   Syntax:
%   rs = pg_fetch_struct(conn, sql, varargin)
%
%   Input:
%   conn      = Database connection object
%   sql       = SQL query string
%   varargin  = none
%
%   Output:
%   rs        = Fetched data from result set from SQL query
%
%   Example
%   conn = pg_connectdb('someDatabase');
%   pg_fetch_struct(conn, 'SELECT * FROM someTable');
%
%   See also pg_exec, pg_select_struct, pg_insert_struct, pg_update_struct, fetch, pg_fetch2struct

%% Copyright notice
%   --------------------------------------------------------------------
%   Copyright (C) 2012 Deltares
%       Bas Hoonhout
%
%       bas.hoonhout@deltares.nl
%
%       Rotterdamseweg 185
%       2629HD Delft
%       Netherlands
%
%   JDBC: Copyright (C) 2012 Deltares for Building with Nature
%       Gerben J. de Boer
%       gerben.deboer@deltares.nl
%
%   Copyright (C) 2013 Nederlands eScience Center
%       Stefan Verhoeven
%       s.verhoeven@esciencecenter.nl
%
%   This library 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 3 of the License, or
%   (at your option) any later version.
%
%   This library 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 library.  If not, see <http://www.gnu.org/licenses/>.
%   --------------------------------------------------------------------

% This tool is part of <a href="http://www.OpenEarth.eu">OpenEarthTools</a>.
% OpenEarthTools is an online collaboration to share and manage data and
% programming tools in an open source, version controlled environment.
% Sign up to recieve regular updates of this function, and to contribute
% your own tools.

%% Version <http://svnbook.red-bean.com/en/1.5/svn.advanced.props.special.keywords.html>
% Created: 27 Jul 2012
% Created with Matlab version: 7.14.0.739 (R2012a)

% $Id: pg_fetch.m 7705 2012-11-20 12:54:51Z boer_g $
% $Date: 2012-11-20 13:54:51 +0100 (Tue, 20 Nov 2012) $
% $Author: boer_g $
% $Revision: 7705 $
% $HeadURL: https://svn.oss.deltares.nl/repos/openearthtools/trunk/matlab/io/postgresql/pg_fetch.m $
% $Keywords: $

%% execute sql query

prefs = getpref('postgresql');

% inspect conn object to find out whether is was created with
% the licensed database toolbox or the JDCB driver directly.
OPT.database_toolbox = 0;
try
    if any(strfind(char(conn.Constructor),'mathworks'))
        OPT.database_toolbox = 1;
    end
end

if OPT.database_toolbox
    setdbprefs('DataReturnFormat','structure')
    rs = fetch(conn, sql);
else
    % http://docs.oracle.com/javase/7/docs/api/java/sql/PreparedStatement.html
    pstat = conn.prepareStatement(sql,...
                                  java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,...
                                  java.sql.ResultSet.CONCUR_READ_ONLY);
    rsraw = pstat.executeQuery();

    % get row count
    rsraw.last();
    nRows = rsraw.getRow();
    rsraw.beforeFirst();

    % http://docs.oracle.com/javase/7/docs/api/java/sql/ResultSetMetaData.html
    nCols = rsraw.getMetaData().getColumnCount();
    rs = struct();
    for iCol=1:nCols
        names{iCol} = char(rsraw.getMetaData().getColumnName(iCol));
        typs{iCol} = char(rsraw.getMetaData().getColumnTypeName(iCol));
        % pre-allocate
        if strncmp(typs{iCol},'float',5) || strncmp(typs{iCol},'numeric',7)
            rs.(names{iCol}) = repmat(NaN,[nRows,1]);
        elseif strncmp(typs{iCol},'int',3)
            rs.(names{iCol}) = repmat(NaN,[nRows,1]);
        else
            rs.(names{iCol}) = cell(nRows,1);
        end
    end

    count=0;
    while rsraw.next()
        count=count+1;
        for iCol=1:nCols
            if strncmp(typs{iCol},'float',5) || strncmp(typs{iCol},'numeric',7)
                rs.(names{iCol})(count)=rsraw.getDouble(iCol);
            elseif strncmp(typs{iCol},'int',3)
                rs.(names{iCol})(count)=rsraw.getInt(iCol);
            else
                rs.(names{iCol}){count}=char(rsraw.getString(iCol));
            end
        end
    end

    pstat.close();
    rsraw.close();

end

if isstruct(prefs) && isfield(prefs, 'verbose') && prefs.verbose
    disp(sql);
end

if isstruct(prefs) && isfield(prefs, 'file') && ~isempty(prefs.file)
    fid = fopen(prefs.file, 'a');
    fprintf(fid, '%s\n', sql);
    fclose(fid);
end
