On Tuesday 20 September 2016 10:21:54 Alex Peshkoff wrote:
> > A convenient solution of the dilemma would be if openCursor() would work
> > with any SQL statement.
> > It seems that a part of that solution already works, calling openCursor()
> > with "
> > insert into TABLE1 (STR1) values ('aabbccdd') returning PK
> > "
> > and getting result metadata by calling getMetadata() on the returned
> > IResultSet actually returns a valid buffer description. What apparently
> > not works yet is calling fetchNext(), it throws a
> > "
> > Cursor is not open
> > "
> > error.
>
> I suppose it's possible to have pseudo-cursor in mentioned case, but
> telling true at the first look the fact that openCursor() does not fail
> for such statement looks like a bug. And your solution is anyway
> incomplete - if SQL has input parameters you anyway need prepare to
> execute it.
>
No, input parameters are supplied by the user by "TParams" class where the 
types are known. MSEgui uses an IMessageMetadata descendant in order to 
supply the input data, please see attachment.

> On the other hand in FB4 we are going to add interface making it
> possible to execute really any SQL statement without explicit prepare
> before it (as a side effect of batch API). Therefore I suggest not to
> use hack-like tricks but wait for next version. I'm sure that fb4
> release cycle will be _much_ shorter compared with fb3.
>
Hmm, please no offence meant, but FB has the reputation to be "not usable over 
the Internet". We know why. ;-)
I think if an exec-like API function works for any statement-kind it is no 
hack but orthogonal design.

Martin
{ MSEgui Copyright (c) 2016 by Martin Schreiber

    See the file COPYING.MSE, included in this distribution,
    for details about the copyright.

    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.
}
//
// specialised api interfaces
//
unit msefbinterface;
{$ifdef FPC}{$mode delphi}{$h+}{$endif}
interface
uses
 firebird,msetypes,msefbconnection,mdb,msedb,msestrings;
type

 paraminfoty = record
  _type: card32;
  subtype: int32;
  scale: int32;
  charset: card32;
  _length: card32;
  offset: card32;
  nulloffset: card32;
  _isnull: boolean;
 end;
 paraminfoarty = array of paraminfoty;
 
 tparamdata = class(imessagemetadataimpl)
  private
   frefcount: int32;
   fparambuffer: pointer;
   fitems: paraminfoarty;
   fcount: int32;
   fmessagelength: int32;
  public
   constructor create(const cursor: tfbcursor; const params: tmseparams);
   destructor destroy(); override;
   procedure addRef() override;
   function release(): Integer override;
   function getCount(status: IStatus): Cardinal override;
   function getField(status: IStatus; index: Cardinal): PAnsiChar override;
   function getRelation(status: IStatus; index: Cardinal): PAnsiChar override;
   function getOwner(status: IStatus; index: Cardinal): PAnsiChar override;
   function getAlias(status: IStatus; index: Cardinal): PAnsiChar override;
   function getType(status: IStatus; index: Cardinal): Cardinal override;
   function isNullable(status: IStatus; index: Cardinal): Boolean override;
   function getSubType(status: IStatus; index: Cardinal): Integer override;
   function getLength(status: IStatus; index: Cardinal): Cardinal override;
   function getScale(status: IStatus; index: Cardinal): Integer override;
   function getCharSet(status: IStatus; index: Cardinal): Cardinal override;
   function getOffset(status: IStatus; index: Cardinal): Cardinal override;
   function getNullOffset(status: IStatus; index: Cardinal): Cardinal override;
   function getBuilder(status: IStatus): IMetadataBuilder override;
   function getMessageLength(status: IStatus): Cardinal override;
   property parambuffer: pointer read fparambuffer;
 end;

 tversioncallback = class(iversioncallbackimpl)
  private
   ftext: string;
  public
   procedure callback(status: IStatus; atext: PAnsiChar); override;
   property text: string read ftext;
 end;
 
implementation
uses
 msefirebird,dbconst,sysutils,msedate;
 
type
 tfbcursor1 = class(tfbcursor);
 tfbconnection1 = class(tfbconnection);
 
{ tparamdata }

constructor tparamdata.create(const cursor: tfbcursor;
                                                  const params: tmseparams);
var
 i1: int32;
 data: stringarty;
 sqltype,sqllen: card32;
 po1: pointer;
 totsize1: int32;
 align1: int32;
 str1,str2: string;
 dt1: tdatetime;
 
begin
 inherited create();
 addref();
 with tfbcursor1(cursor) do begin
  fcount:= length(fparambinding);
  totsize1:= 0;
  if fcount > 0 then begin
   setlength(fitems,fcount);
   setlength(data,fcount); //string buffer
   for i1:= 0 to fcount-1 do begin
    sqltype:= 0;
    sqllen:= 0;
    align1:= 0;
    with params[fparambinding[i1]],fitems[i1] do begin
     _isnull:= isnull;
     scale:= 0;
     case datatype of
      ftunknown: begin
       if isnull then begin
        sqltype:= SQL_NULL;
       end;
      end;
      ftboolean: begin
       sqltype:= SQL_BOOLEAN+1;
       sqllen:= 1;
      end;
      ftinteger,ftsmallint,ftword: begin
       sqltype:= SQL_LONG+1;
       sqllen:= 4;
       align1:= 3;
      end;
      ftlargeint,ftbcd: begin
       sqltype:= SQL_INT64+1;
       sqllen:= 8;
       if datatype = ftbcd then begin
        scale:= -4;
       end;
       case blobkind of
        bk_binary: begin
         subtype:= isc_blob_untyped;
         align1:= 3; //sizeof(SLONG), SLONG always 32 bit
        end;
        bk_text: begin
         subtype:= isc_blob_text;
         align1:= 3; //sizeof(SLONG), SLONG always 32 bit
        end;
        else begin
         align1:= 7;
        end;
       end;
      end;
      ftfloat: begin
       sqltype:= SQL_DOUBLE+1;
       sqllen:= sizeof(double);
       align1:= FB_DOUBLE_ALIGN-1;
      end;
      fttime,ftdate,ftdatetime: begin
       sqltype:= SQL_TIMESTAMP+1;
       sqllen:= sizeof(ISC_TIMESTAMP);
       align1:= sizeof(ISC_DATE)-1;
      end;
      ftstring,ftwidestring,ftmemo,ftwidememo: begin
       sqltype:= SQL_TEXT+1;
       if not isnull then begin
        data[i1]:= params.asdbstring(fparambinding[i1]);
        sqllen:= length(data[i1]);
       end;
      end;
      ftblob,ftgraphic,ftbytes,ftvarbytes,ftguid: begin
       sqltype:= SQL_TEXT+1;
       charset:= cs_binary;
       if not isnull then begin
        if datatype = ftguid then begin
         setlength(data[i1],sizeof(tguid));
         pguid(pointer(data[i1]))^:= dbstringtoguid(asstring);
        end
        else begin
         data[i1]:= asstring;
        end;
        sqllen:= length(data[i1]);
       end;
      end;
     end;
     if sqltype = 0 then begin
      databaseerrorfmt(sunsupportedparameter,[fieldtypenames[datatype]],
                                                                 fconnection);
     end;
     _type:= sqltype;
     _length:= sqllen;
     totsize1:= (totsize1 + align1) and not align1;
     offset:= totsize1;
     totsize1:= totsize1 + sqllen;
     totsize1:= (totsize1 + 1) and not 1;
     nulloffset:= totsize1;
     totsize1:= totsize1 + 2;
    end;
   end;
   getmem(fparambuffer,totsize1);
   fmessagelength:= totsize1;
   for i1:= 0 to fcount-1 do begin
    with fitems[i1] do begin
     if _type <> SQL_NULL then begin
      po1:= fparambuffer + offset;
      with params[i1] do begin
       pisc_short(fparambuffer+nulloffset)^:= card8(_isnull);
       if _isnull then begin
        if blobkind <> bk_none then begin
         _type:= SQL_BLOB+1;
        end;
       end
       else begin
        case _type of
         SQL_BOOLEAN+1: begin
          pcard8(po1)^:= card8(asboolean);
         end;
         SQL_LONG+1: begin
          pint32(po1)^:= asinteger;
         end;
         SQL_INT64+1: begin
          if blobkind <> bk_none then begin
           _type:= SQL_BLOB+1;
           pisc_quad(po1)^:= ISC_QUAD(aslargeint);
          end
          else begin
           if scale = -4 then begin
            pcurrency(po1)^:= ascurrency;
           end
           else begin
            pint64(po1)^:= aslargeint;
           end;
          end;         
         end;
         SQL_DOUBLE+1: begin
          pdouble(po1)^:= asfloat;
         end;
         SQL_TIMESTAMP+1: begin
          dt1:= asdatetime;
          pisc_timestamp(po1)^.timestamp_date:= trunc(dt1) - fbdatetimeoffset;
          dt1:= abs(frac(dt1));
          pisc_timestamp(po1)^.timestamp_time:= 
                    round(dt1*3600*24*ISC_TIME_SECONDS_PRECISION);
         end;
         SQL_TEXT+1: begin
          move(pointer(data[i1])^,po1^,length(data[i1]));
         end;
         SQL_BLOB+1: begin
          if subtype = isc_blob_text then begin
           str1:= params.asdbstring(i1);
          end
          else begin
           str1:= asstring;
          end;
          tfbconnection1(fconnection).writeblobdata(cursor.ftrans,'',nil,
                                      pointer(str1),length(str1),nil,nil,str2);
          pisc_quad(po1)^:= pisc_quad(pointer(str2))^;
         end;
         else begin
          raise exception.create('Internal error 20160908A');
         end;
        end;
       end;
      end;
     end;
    end;
   end;
  end;
 end;
end;

destructor tparamdata.destroy();
begin
 inherited;
 if fparambuffer <> nil then begin
  freemem(fparambuffer);
 end;
end;

procedure tparamdata.addRef();
begin
 inc(frefcount);
end;

function tparamdata.release(): Integer;
begin
 dec(frefcount);
 result:= frefcount;
 if frefcount = 0 then begin
  destroy();
 end;
end;

function tparamdata.getCount(status: IStatus): Cardinal;
begin
 result:= fcount;
end;

function tparamdata.getField(status: IStatus;
               index: Cardinal): PAnsiChar;
begin
 result:= nil;
end;

function tparamdata.getRelation(status: IStatus;
               index: Cardinal): PAnsiChar;
begin
 result:= nil;
end;

function tparamdata.getOwner(status: IStatus;
               index: Cardinal): PAnsiChar;
begin
 result:= nil;
end;

function tparamdata.getAlias(status: IStatus;
               index: Cardinal): PAnsiChar;
begin
 result:= nil;
end;

function tparamdata.getType(status: IStatus;
               index: Cardinal): Cardinal;
begin
 result:= fitems[index]._type;
end;

function tparamdata.isNullable(status: IStatus;
               index: Cardinal): Boolean;
begin
 result:= fitems[index]._type and 1 <> 0;
end;

function tparamdata.getSubType(status: IStatus;
               index: Cardinal): Integer;
begin
 result:= fitems[index].subtype;
end;

function tparamdata.getLength(status: IStatus;
               index: Cardinal): Cardinal;
begin
 result:= fitems[index]._length;
end;

function tparamdata.getScale(status: IStatus;
               index: Cardinal): Integer;
begin
 result:= fitems[index].scale;
end;

function tparamdata.getCharSet(status: IStatus;
               index: Cardinal): Cardinal;
begin
 result:= fitems[index].charset;
end;

function tparamdata.getOffset(status: IStatus;
               index: Cardinal): Cardinal;
begin
 result:= fitems[index].offset;
end;

function tparamdata.getNullOffset(status: IStatus;
               index: Cardinal): Cardinal;
begin
 result:= fitems[index].nulloffset;
end;

function tparamdata.getBuilder(status: IStatus): IMetadataBuilder;
begin
 result:= nil;
end;

function tparamdata.getMessageLength(status: IStatus): Cardinal;
begin
 result:= fmessagelength;
end;

{ tversioncallback }

procedure tversioncallback.callback(status: IStatus; atext: PAnsiChar);
begin
 ftext:= ftext+atext+lineend;
end;

end.
------------------------------------------------------------------------------
Firebird-Devel mailing list, web interface at 
https://lists.sourceforge.net/lists/listinfo/firebird-devel

Reply via email to