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