mORMot and Open Source friends
Check-in [bb7e33b0aa]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:{2018} included and fixed lasted mpv's patches for SynDB
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: bb7e33b0aaab64c2055103b41076da5beb09ef77
User & Date: ab 2015-11-02 10:21:12
Context
2015-11-02
10:29
{2019} fixed FPC compilation after mpv's patches for SynDB check-in: 84b1e1be05 user: ab tags: trunk
10:21
{2018} included and fixed lasted mpv's patches for SynDB check-in: bb7e33b0aa user: ab tags: trunk
2015-10-27
18:40
SynDBZeos: PostgreSQL array binding for select statements check-in: a4c1803024 user: pavel.mash tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SynDBOracle.pas.

1298
1299
1300
1301
1302
1303
1304

1305
1306
1307
1308
1309
1310
1311
....
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
  // If OCI_OBJECTCOPY_NONULL is specified when freeing an instance, the null
  // structure is not freed.
  OCI_OBJECTFREE_FORCE : ub2 = $0001;
  OCI_OBJECTFREE_NONULL: ub2 = $0002;
  OCI_OBJECTFREE_HEADER: ub2 = $0004;

  OCI_PREP2_CACHE_SEARCHONLY: ub4 = $0010;

type
  /// Oracle native number low-level representation
  OCINumber = packed record
    OCINumberPart: array [0..OCI_NUMBER_SIZE-1] of ub1;
  end;

{ TSQLDBOracleLib }
................................................................................
    // 2. prepare statement
    Env := (Connection as TSQLDBOracleConnection).fEnv;
    with OCI do begin
      HandleAlloc(Env,fError,OCI_HTYPE_ERROR);
      if fUseServerSideStatementCache then begin
        if StmtPrepare2(TSQLDBOracleConnection(Connection).fContext,fStatement,
          fError,pointer(oSQL),length(oSQL),nil,0,OCI_NTV_SYNTAX,OCI_PREP2_CACHE_SEARCHONLY) = OCI_SUCCESS then
          SynDBLog.Add.Log(sllDebug, 'Statemet cache HIT')
        else begin
          Check(nil,self,StmtPrepare2(TSQLDBOracleConnection(Connection).fContext,fStatement,
            fError,pointer(oSQL),length(oSQL),nil,0,OCI_NTV_SYNTAX,OCI_DEFAULT),fError);
          SynDBLog.Add.Log(sllDebug, 'Statemet cache miss');
        end;
      end else begin
        HandleAlloc(Env,fStatement,OCI_HTYPE_STMT);
        Check(nil,self,StmtPrepare(fStatement,fError,pointer(oSQL),length(oSQL),
          OCI_NTV_SYNTAX,OCI_DEFAULT),fError);
      end;
    end;






>







 







|



|







1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
....
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
  // If OCI_OBJECTCOPY_NONULL is specified when freeing an instance, the null
  // structure is not freed.
  OCI_OBJECTFREE_FORCE : ub2 = $0001;
  OCI_OBJECTFREE_NONULL: ub2 = $0002;
  OCI_OBJECTFREE_HEADER: ub2 = $0004;

  OCI_PREP2_CACHE_SEARCHONLY: ub4 = $0010;

type
  /// Oracle native number low-level representation
  OCINumber = packed record
    OCINumberPart: array [0..OCI_NUMBER_SIZE-1] of ub1;
  end;

{ TSQLDBOracleLib }
................................................................................
    // 2. prepare statement
    Env := (Connection as TSQLDBOracleConnection).fEnv;
    with OCI do begin
      HandleAlloc(Env,fError,OCI_HTYPE_ERROR);
      if fUseServerSideStatementCache then begin
        if StmtPrepare2(TSQLDBOracleConnection(Connection).fContext,fStatement,
          fError,pointer(oSQL),length(oSQL),nil,0,OCI_NTV_SYNTAX,OCI_PREP2_CACHE_SEARCHONLY) = OCI_SUCCESS then
          SynDBLog.Add.Log(sllDebug, 'Statement cache HIT')
        else begin
          Check(nil,self,StmtPrepare2(TSQLDBOracleConnection(Connection).fContext,fStatement,
            fError,pointer(oSQL),length(oSQL),nil,0,OCI_NTV_SYNTAX,OCI_DEFAULT),fError);
          SynDBLog.Add.Log(sllDebug, 'Statement cache miss');
        end;
      end else begin
        HandleAlloc(Env,fStatement,OCI_HTYPE_STMT);
        Check(nil,self,StmtPrepare(fStatement,fError,pointer(oSQL),length(oSQL),
          OCI_NTV_SYNTAX,OCI_DEFAULT),fError);
      end;
    end;

Changes to SynDBZEOS.pas.

1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
    raise ESQLDBZEOS.CreateUTF8('%.BindArray() not supported',[self]) else
  {$endif}
    for i := 1 to fParamCount do
    with fParams[i-1] do begin
      if (Length(VArray)>0) and (fConnection.Properties.DBMS = dPostgreSQL) then begin
        case VType of
        ftInt64, ftUTF8: VData := UTF8Array2PostgreArray(VArray);
        else raise ESQLDBZEOS.CreateUTF8('%s.ExecutePrepared: Invalid array type on bound parameter #%d', [Self,i]);
        end;
        VType := ftUTF8;
      end;
      case VType of
      ftNull:     fStatement.SetNull(i,stUnknown);
      ftInt64:    fStatement.SetLong(i,VInt64);
      ftDouble:   fStatement.SetDouble(i,PDouble(@VInt64)^);






|







1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
    raise ESQLDBZEOS.CreateUTF8('%.BindArray() not supported',[self]) else
  {$endif}
    for i := 1 to fParamCount do
    with fParams[i-1] do begin
      if (Length(VArray)>0) and (fConnection.Properties.DBMS = dPostgreSQL) then begin
        case VType of
        ftInt64, ftUTF8: VData := UTF8Array2PostgreArray(VArray);
        else raise ESQLDBZEOS.CreateUTF8('%.ExecutePrepared: Invalid array type on bound parameter #%', [Self,i]);
        end;
        VType := ftUTF8;
      end;
      case VType of
      ftNull:     fStatement.SetNull(i,stUnknown);
      ftInt64:    fStatement.SetLong(i,VInt64);
      ftDouble:   fStatement.SetDouble(i,PDouble(@VInt64)^);

Changes to SynOleDB.pas.

25
26
27
28
29
30
31

32
33
34
35
36
37
38
...
248
249
250
251
252
253
254

255
256
257
258
259
260
261
...
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
...
449
450
451
452
453
454
455

456
457
458
459
460
461
462
...
660
661
662
663
664
665
666

667
668
669
670
671
672
673
...
923
924
925
926
927
928
929
930

931
932
933
934
935
936
937
....
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
....
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
....
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
....
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
....
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
....
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945

1946
1947
1948
1949
1950
1951
1952
1953
1954

1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966

1967
1968
1969
1970


1971
1972
1973




1974
1975
1976
1977
1978
1979
1980
1981
....
2406
2407
2408
2409
2410
2411
2412

2413
2414
2415
2416
2417
2418
2419
2420
....
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
....
3073
3074
3075
3076
3077
3078
3079
3080
3081

3082

3083
3084
3085
3086
3087
3088
3089
....
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175

3176
3177
3178
3179
3180
3181
3182
  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2015
  the Initial Developer. All Rights Reserved.

  Contributor(s):


  Alternatively, the contents of this file may be used under the terms of
  either the GNU General Public License Version 2 or later (the "GPL"), or
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  in which case the provisions of the GPL or the LGPL are applicable instead
  of those above. If you wish to allow use of your version of this file only
  under the terms of either the GPL or the LGPL, and not to allow others to
................................................................................
  DBKIND_GUID_NAME     = 0;
  DBKIND_GUID_PROPID   = ( DBKIND_GUID_NAME + 1 );
  DBKIND_NAME          = ( DBKIND_GUID_PROPID + 1 );
  DBKIND_PGUID_NAME    = ( DBKIND_NAME + 1 );
  DBKIND_PGUID_PROPID  = ( DBKIND_PGUID_NAME + 1 );
  DBKIND_PROPID        = ( DBKIND_PGUID_PROPID + 1 );
  DBKIND_GUID          = ( DBKIND_PROPID + 1 );

type
  /// indicates whether the data value or some other value, such as a NULL,
  // is to be used as the value of the column or parameter
  // - see http://msdn.microsoft.com/en-us/library/ms722617
  // and http://msdn.microsoft.com/en-us/library/windows/desktop/ms716934
  TOleDBStatus = (
    stOK, stBadAccessor, stCanNotConvertValue, stIsNull, stTruncated,
................................................................................
  DBPROPID = UINT;
  DBPROPOPTIONS = UINT;
  DBCOLUMNFLAGS = UINT;
  DBKIND = UINT;
  DBSTATUS = DWORD;
  DBPARAMFLAGS = DWORD;
  DBTYPE = Word;
  DBRESULTFLAG = LONG;
  PBoid = ^TBoid;
{$ifdef CPU64}
  {$A+} // un-packed records
{$else}
  {$A-} // packed records
{$endif}
  TBoid = record
................................................................................
    bScale: Byte;
  end;
  TDBParamBindInfo = DBPARAMBINDINFO;

  PDBParamBindInfoArray = ^TDBParamBindInfoArray;
  TDBParamBindInfoArray = array[0..MAXBOUND] of TDBParamBindInfo;
  TDBParamBindInfoDynArray = array of TDBParamBindInfo;

{$ifndef CPU64}
  {$A+} // packed records
{$endif}

  /// initialize and uninitialize OleDB data source objects and enumerators
  IDBInitialize = interface(IUnknown)
    ['{0C733A8B-2A1C-11CE-ADE5-00AA0044773D}']
................................................................................
  /// will implement properties shared by OleDB connections
  TOleDBConnectionProperties = class(TSQLDBConnectionPropertiesThreadSafe)
  protected
    fProviderName: RawUTF8;
    fConnectionString: SynUnicode;
    fOnCustomError: TOleDBOnCustomError;
    fSchemaRec: array of TDBSchemaRec;

    function GetSchema(const aUID: TGUID; const Fields: array of RawUTF8;
      var aResult: IRowSet): boolean;
    /// will create the generic fConnectionString from supplied parameters
    procedure SetInternalProperties; override;
    /// initialize fForeignKeys content with all foreign keys of this DB
    // - used by GetForeignKey method
    procedure GetForeignKeys; override;
................................................................................
    // does work... so we'll use it here! Shame on Microsoft!
    // - what's fine with DBTYPE_BSTR is that it can be resized by the provider
    // in case of VInOut in [paramOut, paramInOut] - so let it be
    VText: WideString;
    /// storage used for ftInt64, ftDouble, ftDate and ftCurrency value
    VInt64: Int64;
    /// storage used for table variables
    VIUnknown:IUnknown;

    VArray: TRawUTF8DynArray;
    /// storage used for the OleDB status field
    // - if VStatus=ord(stIsNull), then it will bind a NULL with the type
    // as set by VType (to avoid conversion error like in [e8c211062e])
    VStatus: integer;
    /// the column/parameter Value type
    VType: TSQLDBFieldType;
................................................................................
    /// bind a NULL value to a parameter
    // - the leftmost SQL parameter has an index of 1
    // - OleDB during MULTI INSERT statements expect BoundType to be set in
    // TOleDBStatementParam, and its VStatus set to ord(stIsNull)
    // - raise an EOleDBException on any error
    procedure BindNull(Param: Integer; IO: TSQLDBParamInOutType=paramIn;
      BoundType: TSQLDBFieldType=ftNull); override;
    /// bind a array if Int64 values to a parameter
    // using TABLE variable (MSSQl 2008 & UP). Type:
    // CREATE TYPE dbo.IDList AS TABLE(id bigint NULL)
    // must be declareded in the database.
    // Internally BindArray(0, [1, 2,3]) is the same as:
    //  declare @a dbo.IDList;
    //  insert into @a (id) values (1), (2), (3);
    //  SELECT usr.ID   FROM user usr WHERE usr.ID IN  (select id from @a)
    procedure BindArray(Param: Integer;
      const Values: array of Int64); overload; override;
    /// bind a array of RawUTF8 (255 length max) values to a parameter
    // using TABLE variable (MSSQl 2008 & UP). Type:
    // CREATE TYPE dbo.StrList AS TABLE(id nvarchar(255) NULL)
    // must be declareded in the database
    procedure BindArray(Param: Integer;
      const Values: array of RawUTF8); overload; override;
    /// bind an integer value to a parameter
    // - the leftmost SQL parameter has an index of 1
    // - raise an EOleDBException on any error
    procedure Bind(Param: Integer; Value: Int64;
      IO: TSQLDBParamInOutType=paramIn); overload; override;
................................................................................
    // - several rows are retrieved at once into the internal buffer
    // - default value is 16384 bytes, minimal allowed size is 8192
    property RowBufferSize: integer read fRowBufferSize write SetRowBufferSize;
  end;

  TBaseAggregatingRowset = class(TObject, IUnknown, IRowset)
  private
   fcRef: integer;
   fcTotalRows: UINT;
   // Defining as an array because in general there can be as many accessors as necessary
   // the reading rules from the provider for such scenarios are describe in the Books online
   fhAccessor: HACCESSORDynArray;
  protected
   fidxRow: UINT;
   fUnkInnerSQLNCLIRowset: IUnknown;
................................................................................
    // 1. check execution context
    if not Assigned(fCommand) then
      raise EOleDBException.CreateUTF8('%s.Prepare should have been called',[self]);
    if Assigned(fRowSet) or (fColumnCount>0) or
       (fColumnBindings<>nil) or (fParamBindings<>nil) then
      raise EOleDBException.CreateUTF8('Missing call to %.Reset',[self]);
    SetLength(IDLists, fParamCount);
    for i := 0 to fParamCount - 1 do
      IDLists[i] := nil;
    try
      // 2. bind parameters
      if fParamCount=0 then
        // no parameter to bind
        fDBParams.cParamSets := 0 else begin
        // bind supplied parameters, with direct mapping to fParams[]
        for i := 0 to fParamCount-1 do
................................................................................
        P := pointer(fParams);
        SetLength(fParamBindings,fParamCount);
        B := pointer(fParamBindings);
        SetLength(fParamBindInfo, fParamCount);
        BI := pointer(fParamBindInfo);
        SetLength(fParamOrdinals, fParamCount);
        PO := pointer(fParamOrdinals);

        dbObjTVP.dwFlags := STGM_READ;
        dbObjTVP.iid := IID_IRowset;

        FillChar(ssPropParamIDList,SizeOf(ssPropParamIDList),0);
        ssPropParamIDList.dwPropertyID := SSPROP_PARAM_TYPE_TYPENAME;
        ssPropParamIDList.vValue := 'IDList';//This type must be declared in DB

        ssPropsetParamIDList.cProperties := 1;
        ssPropsetParamIDList.guidPropertySet := DBPROPSET_SQLSERVERPARAMETER;
        ssPropsetParamIDList.rgProperties := @ssPropParamIDList;

        FillChar(ssPropParamStrList,SizeOf(ssPropParamStrList),0);
        ssPropParamStrList.dwPropertyID := SSPROP_PARAM_TYPE_TYPENAME;
        ssPropParamStrList.vValue := 'StrList';//This type must be declared in DB

        ssPropsetParamStrList.cProperties := 1;
        ssPropsetParamStrList.guidPropertySet := DBPROPSET_SQLSERVERPARAMETER;
        ssPropsetParamStrList.rgProperties := @ssPropParamStrList;

        SetLength(ssParamProps, fParamCount);
        ssParamPropsCount := 0;
        for i := 1 to fParamCount do begin
          B^.iOrdinal := i; // parameter index (starting at 1)
          B^.eParamIO := PARAMTYPE2OLEDB[P^.VInOut]; // parameter direction
          B^.wType := FIELDTYPE2OLEDB[P^.VType];     // parameter data type
          B^.dwPart := DBPART_VALUE or DBPART_STATUS;
          B^.obValue := PAnsiChar(@P^.VInt64)-pointer(fParams);
          B^.obStatus := PAnsiChar(@P^.VStatus)-pointer(fParams);

          BI^.dwFlags := PARAMTYPE2OLEDB[P^.VInOut]; // parameter direction
          BI^.pwszName := nil; //unnamed parameters
          BI^.pwszDataSourceType :=  Pointer(FIELDTYPE2OLEDBTYPE_NAME[P^.VType]) ;
          BI^.ulParamSize := 0;
          PO^ := i;
          // check array binding
          if Length(P.VArray) >0 then begin
................................................................................
              else raise EOleDBException.Create('Unsupported array parameter type');
            end;
            ssParamProps[ssParamPropsCount].cPropertySets := 1;
            ssParamProps[ssParamPropsCount].iOrdinal := i;
            inc(ssParamPropsCount);
            IDLists[i-1] := TIDListRowset.Create(P.VArray, P^.VType);
            IDLists[i-1].Initialize(OleDBConnection.fSession as IOpenRowset);
            P^.VIUnknown := IDLists[i-1];///***
          end else begin
            P^.VIUnknown := nil;
            case P^.VType of
            ftNull: begin
              P^.VStatus := ord(stIsNull);
              BI.pwszDataSourceType := 'DBTYPE_WVARCHAR';
              BI.dwFlags := BI^.dwFlags or DBPARAMFLAGS_ISNULLABLE;
................................................................................
                BI^.ulParamSize := length(P^.VText);
              end;
            end;
            end;
            if BI^.ulParamSize = 0 then
              BI^.ulParamSize := B^.cbMaxLen;
          end;

          inc(P);
          inc(B);
          inc(BI);
          inc(PO);
        end;

        OleDBConnection.OleDBCheck(self,
          (fCommand as ISSCommandWithParameters).SetParameterInfo(
            fParamCount, pointer(fParamOrdinals), pointer(fParamBindInfo)));

        if ssParamPropsCount>0 then
          OleDBConnection.OleDBCheck(self,
            (fCommand as ISSCommandWithParameters).SetParameterProperties(
              ssParamPropsCount, pointer(ssParamProps)));


        SetLength(ParamsStatus,fParamCount);
        OleDBConnection.OleDBCheck(self,
          (fCommand as IAccessor).CreateAccessor(
            DBACCESSOR_PARAMETERDATA,fParamCount,Pointer(fParamBindings),0,
            fDBParams.HACCESSOR,pointer(ParamsStatus)),ParamsStatus);
        fDBParams.cParamSets := 1;
        fDBParams.pData := pointer(fParams);
      end;
      // 3. Execute SQL
      if fExpectResults then
      try
        // 3.1 SELECT will allow access to resulting rows data from fRowSet

        OleDBConnection.OleDBCheck(self,
  //        fCommand.Execute(nil,IID_IRowset,fDBParams,@fUpdateCount,@RowSet),ParamsStatus);
  //  Use IMultipleResults for 'insert into table1 values (...); select ... from table2 where ...'
          fCommand.Execute(nil,IID_IMultipleResults,fDBParams,@fUpdateCount,@mr),ParamsStatus);


        repeat
          res := mr.GetResult(nil,0,IID_IRowset,@fUpdateCount,@RowSet);
        until Assigned(RowSet) or (res <> S_OK);




        OleDBConnection.OleDBCheck(self, res);
        FromRowSet(RowSet);
      except
        on E: Exception do begin
          CloseRowSet; // force fRowSet=nil
          raise;
        end;
      end else
................................................................................
      end;
  end;
  // get generic HRESULT error
  if not Succeeded(aResult) or (fOleDBErrorMessage<>'') then begin
    s := SysErrorMessage(aResult);
    if s='' then
      s := 'OLEDB Error '+IntToHex(aResult,8);

    fOleDBErrorMessage := s+' - '+fOleDBErrorMessage;
  end;
  if fOleDBErrorMessage='' then
    exit;
  // retrieve binding information from Status[]
  s := '';
  for i := 0 to high(aStatus) do
    if TOleDBBindStatus(aStatus[i])<>bsOK then begin
................................................................................

    end;
  if s<>'' then
    fOleDBErrorMessage :=  fOleDBErrorMessage+s;
  // raise exception
  if aStmt=nil then
    E := EOleDBException.Create(fOleDBErrorMessage) else
    E := EOleDBException.CreateUTF8('%: %',[self,fOleDBErrorMessage]);
  SynDBLog.Add.Log(sllError,E);
  raise E;
end;
begin
  fOleDBErrorMessage := '';
  fOleDBInfoMessage := '';
  if not Succeeded(aResult) or Assigned(OleDBProperties.OnCustomError) then
................................................................................
  farr := arr;
  fType := aType;
  inherited Create(Length(farr ));
end;

procedure TIDListRowset.FillBindingsAndSetupRowBuffer(
  pBindingsList: PDBBindingArray);
var  rec: TIDListRec;
     i: Integer;

begin

  pBindingsList[0].pTypeInfo := nil;
  pBindingsList[0].pObject := nil;
  pBindingsList[0].pBindExt := nil;
  pBindingsList[0].eParamIO := DBPARAMIO_NOTPARAM;
  pBindingsList[0].iOrdinal := 1;
  pBindingsList[0].dwPart := DBPART_VALUE or DBPART_STATUS or DBPART_LENGTH;
  pBindingsList[0].dwMemOwner := DBMEMOWNER_CLIENTOWNED;
................................................................................
      for I := 0 to Length(farr)-1 do
        if Length(farr[i])*SizeOf(WideChar)>Integer(pBindingsList[0].cbMaxLen) then
          pBindingsList[0].cbMaxLen := Length(farr[i])*SizeOf(WideChar);
      pBindingsList[0].obValue := PAnsiChar(@rec.StrVal)-pointer(@rec);
      pBindingsList[0].wType := DBTYPE_BSTR
    end;
  end;

  pBindingsList[0].obStatus := PAnsiChar(@rec.IDST)-pointer(@rec);
  pBindingsList[0].obLength := PAnsiChar(@rec.IDLen)-pointer(@rec);

end;

procedure TIDListRowset.FillRowData(pCurrentRec: PIDListRec);
var
  curInd: Integer;
  tmp: RawUTF8;
begin
  curInd := fidxRow-2;
  if farr[curInd]='null' then begin
    pCurrentRec.IDST := ord(stIsNull);
  end else begin
    pCurrentRec.IDST := 0;
    case fType of
      ftInt64: begin
        SetInt64(pointer(farr[curInd]),pCurrentRec.IDVal);
        pCurrentRec.IDLen := SizeOf(Int64);
      end;
      ftUTF8: begin
          tmp := UnQuoteSQLString(farr[curInd]);
          pCurrentRec.IDLen := (Length(tmp)+1)*SizeOf(WideChar);
          pCurrentRec.StrVal := Pointer(UTF8ToWideString(tmp));
      end
      else raise EOleDBException.Create('Unsupported array parameter type');
    end;
  end;
end;

function TIDListRowset.GetData(HROW: HROW; HACCESSOR: HACCESSOR;
  pData: Pointer): HResult;
var
  currentRec: PIDListRec;
begin
  inherited GetData(HROW, HACCESSOR, pData);
  currentRec := pData;
  FillRowData(currentRec);
  Result := S_OK;
end;

function TIDListRowset.Initialize(pIOpenRowset: IOpenRowset): HRESULT;
var
  dbidID: DBID;
begin
  dbidID.eKind := DBKIND_GUID_NAME;
  dbidID.uGuid.guid := CLSID_ROWSET_TVP;
  case fType of
    ftInt64: dbidID.uName.pwszName := 'IDList';
    ftUTF8: dbidID.uName.pwszName := 'StrList';
  end;
  OleCheck(pIOpenRowset.OpenRowset(self, @dbidID, nil, IID_IUnknown, 0, nil, @fUnkInnerSQLNCLIRowset));
  OleCheck(SetupAccessors(self as IAccessor));
  Result := S_OK;
end;

function TIDListRowset.SetupAccessors(pIAccessorIDList: IAccessor): HRESULT;
var
  binding: array [0..0] of TDBBinding;
  bindStatus: array [0..0] of DWORD;
  hAccessorIDList: HACCESSOR;
begin
  FillBindingsAndSetupRowBuffer(@binding);
  bindStatus[0] := 0;
  OleCheck(pIAccessorIDList.CreateAccessor(DBACCESSOR_ROWDATA, 1, @binding, SizeOf(TIDListRec),
    hAccessorIDList, @bindStatus));
  SetAccessorHandle(0, hAccessorIDList);
  Result := S_OK;
end;


initialization
  assert(sizeof(TOleDBStatementParam) and (sizeof(Int64)-1)=0);
  TOleDBConnectionProperties.RegisterClassNameForDefinition;
  TOleDBOracleConnectionProperties.RegisterClassNameForDefinition;
  TOleDBMSOracleConnectionProperties.RegisterClassNameForDefinition;
  TOleDBMSSQLConnectionProperties.RegisterClassNameForDefinition;






>







 







>







 







|







 







>







 







>







 







|
>







 







|
|
|
<
|
|
|
|



|
|
|







 







<







 







<
<







 







<


<



<



<



<



<









<







 







|







 







<





>
|
|
|
<
|
|
|
|
<
>












>
|
<
|
|
>
>
|
|
|
>
>
>
>
|







 







>
|







 







|







 







<
|
>

>







 







<


<



<
|
|












|
|
|








<
|








<
|





|







<
|
|
|








>







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
...
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
...
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
...
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
...
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
...
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
....
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028

1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
....
1185
1186
1187
1188
1189
1190
1191

1192
1193
1194
1195
1196
1197
1198
....
1828
1829
1830
1831
1832
1833
1834


1835
1836
1837
1838
1839
1840
1841
....
1846
1847
1848
1849
1850
1851
1852

1853
1854

1855
1856
1857

1858
1859
1860

1861
1862
1863

1864
1865
1866

1867
1868
1869
1870
1871
1872
1873
1874
1875

1876
1877
1878
1879
1880
1881
1882
....
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
....
1927
1928
1929
1930
1931
1932
1933

1934
1935
1936
1937
1938
1939
1940
1941
1942

1943
1944
1945
1946

1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961

1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
....
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
....
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
....
3073
3074
3075
3076
3077
3078
3079

3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
....
3100
3101
3102
3103
3104
3105
3106

3107
3108

3109
3110
3111

3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136

3137
3138
3139
3140
3141
3142
3143
3144
3145

3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159

3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2015
  the Initial Developer. All Rights Reserved.

  Contributor(s):
  - Pavel (mpv)

  Alternatively, the contents of this file may be used under the terms of
  either the GNU General Public License Version 2 or later (the "GPL"), or
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  in which case the provisions of the GPL or the LGPL are applicable instead
  of those above. If you wish to allow use of your version of this file only
  under the terms of either the GPL or the LGPL, and not to allow others to
................................................................................
  DBKIND_GUID_NAME     = 0;
  DBKIND_GUID_PROPID   = ( DBKIND_GUID_NAME + 1 );
  DBKIND_NAME          = ( DBKIND_GUID_PROPID + 1 );
  DBKIND_PGUID_NAME    = ( DBKIND_NAME + 1 );
  DBKIND_PGUID_PROPID  = ( DBKIND_PGUID_NAME + 1 );
  DBKIND_PROPID        = ( DBKIND_PGUID_PROPID + 1 );
  DBKIND_GUID          = ( DBKIND_PROPID + 1 );

type
  /// indicates whether the data value or some other value, such as a NULL,
  // is to be used as the value of the column or parameter
  // - see http://msdn.microsoft.com/en-us/library/ms722617
  // and http://msdn.microsoft.com/en-us/library/windows/desktop/ms716934
  TOleDBStatus = (
    stOK, stBadAccessor, stCanNotConvertValue, stIsNull, stTruncated,
................................................................................
  DBPROPID = UINT;
  DBPROPOPTIONS = UINT;
  DBCOLUMNFLAGS = UINT;
  DBKIND = UINT;
  DBSTATUS = DWORD;
  DBPARAMFLAGS = DWORD;
  DBTYPE = Word;
  DBRESULTFLAG = UINT;
  PBoid = ^TBoid;
{$ifdef CPU64}
  {$A+} // un-packed records
{$else}
  {$A-} // packed records
{$endif}
  TBoid = record
................................................................................
    bScale: Byte;
  end;
  TDBParamBindInfo = DBPARAMBINDINFO;

  PDBParamBindInfoArray = ^TDBParamBindInfoArray;
  TDBParamBindInfoArray = array[0..MAXBOUND] of TDBParamBindInfo;
  TDBParamBindInfoDynArray = array of TDBParamBindInfo;

{$ifndef CPU64}
  {$A+} // packed records
{$endif}

  /// initialize and uninitialize OleDB data source objects and enumerators
  IDBInitialize = interface(IUnknown)
    ['{0C733A8B-2A1C-11CE-ADE5-00AA0044773D}']
................................................................................
  /// will implement properties shared by OleDB connections
  TOleDBConnectionProperties = class(TSQLDBConnectionPropertiesThreadSafe)
  protected
    fProviderName: RawUTF8;
    fConnectionString: SynUnicode;
    fOnCustomError: TOleDBOnCustomError;
    fSchemaRec: array of TDBSchemaRec;
    fSupportsOnlyIRowset: boolean;
    function GetSchema(const aUID: TGUID; const Fields: array of RawUTF8;
      var aResult: IRowSet): boolean;
    /// will create the generic fConnectionString from supplied parameters
    procedure SetInternalProperties; override;
    /// initialize fForeignKeys content with all foreign keys of this DB
    // - used by GetForeignKey method
    procedure GetForeignKeys; override;
................................................................................
    // does work... so we'll use it here! Shame on Microsoft!
    // - what's fine with DBTYPE_BSTR is that it can be resized by the provider
    // in case of VInOut in [paramOut, paramInOut] - so let it be
    VText: WideString;
    /// storage used for ftInt64, ftDouble, ftDate and ftCurrency value
    VInt64: Int64;
    /// storage used for table variables
    VIUnknown: IUnknown;
    /// storage used for table variables
    VArray: TRawUTF8DynArray;
    /// storage used for the OleDB status field
    // - if VStatus=ord(stIsNull), then it will bind a NULL with the type
    // as set by VType (to avoid conversion error like in [e8c211062e])
    VStatus: integer;
    /// the column/parameter Value type
    VType: TSQLDBFieldType;
................................................................................
    /// bind a NULL value to a parameter
    // - the leftmost SQL parameter has an index of 1
    // - OleDB during MULTI INSERT statements expect BoundType to be set in
    // TOleDBStatementParam, and its VStatus set to ord(stIsNull)
    // - raise an EOleDBException on any error
    procedure BindNull(Param: Integer; IO: TSQLDBParamInOutType=paramIn;
      BoundType: TSQLDBFieldType=ftNull); override;
    /// bind an array of Int64 values to a parameter
    // - using TABLE variable (MSSQl 2008 & UP). Must be created in the database as:
    // $ CREATE TYPE dbo.IDList AS TABLE(id bigint NULL)

    // - Internally BindArray(0, [1, 2,3]) is the same as:
    // $ declare @a dbo.IDList;
    // $ insert into @a (id) values (1), (2), (3);
    // $ SELECT usr.ID   FROM user usr WHERE usr.ID IN  (select id from @a)
    procedure BindArray(Param: Integer;
      const Values: array of Int64); overload; override;
    /// bind a array of RawUTF8 (255 length max) values to a parameter
    // - using TABLE variable (MSSQl 2008 & UP). Must be created in the database as:
    // $ CREATE TYPE dbo.StrList AS TABLE(id nvarchar(255) NULL)
    // - must be declareded in the database
    procedure BindArray(Param: Integer;
      const Values: array of RawUTF8); overload; override;
    /// bind an integer value to a parameter
    // - the leftmost SQL parameter has an index of 1
    // - raise an EOleDBException on any error
    procedure Bind(Param: Integer; Value: Int64;
      IO: TSQLDBParamInOutType=paramIn); overload; override;
................................................................................
    // - several rows are retrieved at once into the internal buffer
    // - default value is 16384 bytes, minimal allowed size is 8192
    property RowBufferSize: integer read fRowBufferSize write SetRowBufferSize;
  end;

  TBaseAggregatingRowset = class(TObject, IUnknown, IRowset)
  private

   fcTotalRows: UINT;
   // Defining as an array because in general there can be as many accessors as necessary
   // the reading rules from the provider for such scenarios are describe in the Books online
   fhAccessor: HACCESSORDynArray;
  protected
   fidxRow: UINT;
   fUnkInnerSQLNCLIRowset: IUnknown;
................................................................................
    // 1. check execution context
    if not Assigned(fCommand) then
      raise EOleDBException.CreateUTF8('%s.Prepare should have been called',[self]);
    if Assigned(fRowSet) or (fColumnCount>0) or
       (fColumnBindings<>nil) or (fParamBindings<>nil) then
      raise EOleDBException.CreateUTF8('Missing call to %.Reset',[self]);
    SetLength(IDLists, fParamCount);


    try
      // 2. bind parameters
      if fParamCount=0 then
        // no parameter to bind
        fDBParams.cParamSets := 0 else begin
        // bind supplied parameters, with direct mapping to fParams[]
        for i := 0 to fParamCount-1 do
................................................................................
        P := pointer(fParams);
        SetLength(fParamBindings,fParamCount);
        B := pointer(fParamBindings);
        SetLength(fParamBindInfo, fParamCount);
        BI := pointer(fParamBindInfo);
        SetLength(fParamOrdinals, fParamCount);
        PO := pointer(fParamOrdinals);

        dbObjTVP.dwFlags := STGM_READ;
        dbObjTVP.iid := IID_IRowset;

        FillChar(ssPropParamIDList,SizeOf(ssPropParamIDList),0);
        ssPropParamIDList.dwPropertyID := SSPROP_PARAM_TYPE_TYPENAME;
        ssPropParamIDList.vValue := 'IDList';//This type must be declared in DB

        ssPropsetParamIDList.cProperties := 1;
        ssPropsetParamIDList.guidPropertySet := DBPROPSET_SQLSERVERPARAMETER;
        ssPropsetParamIDList.rgProperties := @ssPropParamIDList;

        FillChar(ssPropParamStrList,SizeOf(ssPropParamStrList),0);
        ssPropParamStrList.dwPropertyID := SSPROP_PARAM_TYPE_TYPENAME;
        ssPropParamStrList.vValue := 'StrList';//This type must be declared in DB

        ssPropsetParamStrList.cProperties := 1;
        ssPropsetParamStrList.guidPropertySet := DBPROPSET_SQLSERVERPARAMETER;
        ssPropsetParamStrList.rgProperties := @ssPropParamStrList;

        SetLength(ssParamProps, fParamCount);
        ssParamPropsCount := 0;
        for i := 1 to fParamCount do begin
          B^.iOrdinal := i; // parameter index (starting at 1)
          B^.eParamIO := PARAMTYPE2OLEDB[P^.VInOut]; // parameter direction
          B^.wType := FIELDTYPE2OLEDB[P^.VType];     // parameter data type
          B^.dwPart := DBPART_VALUE or DBPART_STATUS;
          B^.obValue := PAnsiChar(@P^.VInt64)-pointer(fParams);
          B^.obStatus := PAnsiChar(@P^.VStatus)-pointer(fParams);

          BI^.dwFlags := PARAMTYPE2OLEDB[P^.VInOut]; // parameter direction
          BI^.pwszName := nil; //unnamed parameters
          BI^.pwszDataSourceType :=  Pointer(FIELDTYPE2OLEDBTYPE_NAME[P^.VType]) ;
          BI^.ulParamSize := 0;
          PO^ := i;
          // check array binding
          if Length(P.VArray) >0 then begin
................................................................................
              else raise EOleDBException.Create('Unsupported array parameter type');
            end;
            ssParamProps[ssParamPropsCount].cPropertySets := 1;
            ssParamProps[ssParamPropsCount].iOrdinal := i;
            inc(ssParamPropsCount);
            IDLists[i-1] := TIDListRowset.Create(P.VArray, P^.VType);
            IDLists[i-1].Initialize(OleDBConnection.fSession as IOpenRowset);
            P^.VIUnknown := IDLists[i-1];
          end else begin
            P^.VIUnknown := nil;
            case P^.VType of
            ftNull: begin
              P^.VStatus := ord(stIsNull);
              BI.pwszDataSourceType := 'DBTYPE_WVARCHAR';
              BI.dwFlags := BI^.dwFlags or DBPARAMFLAGS_ISNULLABLE;
................................................................................
                BI^.ulParamSize := length(P^.VText);
              end;
            end;
            end;
            if BI^.ulParamSize = 0 then
              BI^.ulParamSize := B^.cbMaxLen;
          end;

          inc(P);
          inc(B);
          inc(BI);
          inc(PO);
        end;
        if not OleDBConnection.OleDBProperties.fSupportsOnlyIRowset then begin
          OleDBConnection.OleDBCheck(self,
            (fCommand as ISSCommandWithParameters).SetParameterInfo(
              fParamCount, pointer(fParamOrdinals), pointer(fParamBindInfo)));

          if ssParamPropsCount>0 then
            OleDBConnection.OleDBCheck(self,
              (fCommand as ISSCommandWithParameters).SetParameterProperties(
                ssParamPropsCount, pointer(ssParamProps)));

        end;
        SetLength(ParamsStatus,fParamCount);
        OleDBConnection.OleDBCheck(self,
          (fCommand as IAccessor).CreateAccessor(
            DBACCESSOR_PARAMETERDATA,fParamCount,Pointer(fParamBindings),0,
            fDBParams.HACCESSOR,pointer(ParamsStatus)),ParamsStatus);
        fDBParams.cParamSets := 1;
        fDBParams.pData := pointer(fParams);
      end;
      // 3. Execute SQL
      if fExpectResults then
      try
        // 3.1 SELECT will allow access to resulting rows data from fRowSet
        res := E_UNEXPECTED; // makes compiler happy
        if not OleDBConnection.OleDBProperties.fSupportsOnlyIRowset then begin

          // use IMultipleResults for 'insert into table1 values (...); select ... from table2 where ...'
          res := fCommand.Execute(nil,IID_IMultipleResults,fDBParams,@fUpdateCount,@mr);
          if res=E_NOINTERFACE then
            OleDBConnection.OleDBProperties.fSupportsOnlyIRowset := true else begin
            repeat
              res := mr.GetResult(nil,0,IID_IRowset,@fUpdateCount,@RowSet);
            until Assigned(RowSet) or (res <> S_OK);
          end;
        end;
        if OleDBConnection.OleDBProperties.fSupportsOnlyIRowset then
          res := fCommand.Execute(nil,IID_IRowset,fDBParams,nil,@RowSet);
        OleDBConnection.OleDBCheck(self,res,ParamsStatus);
        FromRowSet(RowSet);
      except
        on E: Exception do begin
          CloseRowSet; // force fRowSet=nil
          raise;
        end;
      end else
................................................................................
      end;
  end;
  // get generic HRESULT error
  if not Succeeded(aResult) or (fOleDBErrorMessage<>'') then begin
    s := SysErrorMessage(aResult);
    if s='' then
      s := 'OLEDB Error '+IntToHex(aResult,8);
    if s<>fOleDBErrorMessage then
      fOleDBErrorMessage := s+' - '+fOleDBErrorMessage;
  end;
  if fOleDBErrorMessage='' then
    exit;
  // retrieve binding information from Status[]
  s := '';
  for i := 0 to high(aStatus) do
    if TOleDBBindStatus(aStatus[i])<>bsOK then begin
................................................................................

    end;
  if s<>'' then
    fOleDBErrorMessage :=  fOleDBErrorMessage+s;
  // raise exception
  if aStmt=nil then
    E := EOleDBException.Create(fOleDBErrorMessage) else
    E := EOleDBException.CreateUTF8('%: %',[self,StringToUTF8(fOleDBErrorMessage)]);
  SynDBLog.Add.Log(sllError,E);
  raise E;
end;
begin
  fOleDBErrorMessage := '';
  fOleDBInfoMessage := '';
  if not Succeeded(aResult) or Assigned(OleDBProperties.OnCustomError) then
................................................................................
  farr := arr;
  fType := aType;
  inherited Create(Length(farr ));
end;

procedure TIDListRowset.FillBindingsAndSetupRowBuffer(
  pBindingsList: PDBBindingArray);

var i: Integer;
    rec: TIDListRec; // pseudo record to compute offset within TIDListRec
begin
  fillchar(rec,sizeof(rec),0); // makes Win64 compiler happy
  pBindingsList[0].pTypeInfo := nil;
  pBindingsList[0].pObject := nil;
  pBindingsList[0].pBindExt := nil;
  pBindingsList[0].eParamIO := DBPARAMIO_NOTPARAM;
  pBindingsList[0].iOrdinal := 1;
  pBindingsList[0].dwPart := DBPART_VALUE or DBPART_STATUS or DBPART_LENGTH;
  pBindingsList[0].dwMemOwner := DBMEMOWNER_CLIENTOWNED;
................................................................................
      for I := 0 to Length(farr)-1 do
        if Length(farr[i])*SizeOf(WideChar)>Integer(pBindingsList[0].cbMaxLen) then
          pBindingsList[0].cbMaxLen := Length(farr[i])*SizeOf(WideChar);
      pBindingsList[0].obValue := PAnsiChar(@rec.StrVal)-pointer(@rec);
      pBindingsList[0].wType := DBTYPE_BSTR
    end;
  end;

  pBindingsList[0].obStatus := PAnsiChar(@rec.IDST)-pointer(@rec);
  pBindingsList[0].obLength := PAnsiChar(@rec.IDLen)-pointer(@rec);

end;

procedure TIDListRowset.FillRowData(pCurrentRec: PIDListRec);

var curInd: Integer;
    tmp: RawUTF8;
begin
  curInd := fidxRow-2;
  if farr[curInd]='null' then begin
    pCurrentRec.IDST := ord(stIsNull);
  end else begin
    pCurrentRec.IDST := 0;
    case fType of
      ftInt64: begin
        SetInt64(pointer(farr[curInd]),pCurrentRec.IDVal);
        pCurrentRec.IDLen := SizeOf(Int64);
      end;
      ftUTF8: begin
        tmp := UnQuoteSQLString(farr[curInd]);
        pCurrentRec.IDLen := (Length(tmp)+1)*SizeOf(WideChar);
        pCurrentRec.StrVal := Pointer(UTF8ToWideString(tmp));
      end
      else raise EOleDBException.Create('Unsupported array parameter type');
    end;
  end;
end;

function TIDListRowset.GetData(HROW: HROW; HACCESSOR: HACCESSOR;
  pData: Pointer): HResult;

var currentRec: PIDListRec;
begin
  inherited GetData(HROW, HACCESSOR, pData);
  currentRec := pData;
  FillRowData(currentRec);
  Result := S_OK;
end;

function TIDListRowset.Initialize(pIOpenRowset: IOpenRowset): HRESULT;

var dbidID: DBID;
begin
  dbidID.eKind := DBKIND_GUID_NAME;
  dbidID.uGuid.guid := CLSID_ROWSET_TVP;
  case fType of
    ftInt64: dbidID.uName.pwszName := 'IDList';
    ftUTF8:  dbidID.uName.pwszName := 'StrList';
  end;
  OleCheck(pIOpenRowset.OpenRowset(self, @dbidID, nil, IID_IUnknown, 0, nil, @fUnkInnerSQLNCLIRowset));
  OleCheck(SetupAccessors(self as IAccessor));
  Result := S_OK;
end;

function TIDListRowset.SetupAccessors(pIAccessorIDList: IAccessor): HRESULT;

var binding: array [0..0] of TDBBinding;
    bindStatus: array [0..0] of DWORD;
    hAccessorIDList: HACCESSOR;
begin
  FillBindingsAndSetupRowBuffer(@binding);
  bindStatus[0] := 0;
  OleCheck(pIAccessorIDList.CreateAccessor(DBACCESSOR_ROWDATA, 1, @binding, SizeOf(TIDListRec),
    hAccessorIDList, @bindStatus));
  SetAccessorHandle(0, hAccessorIDList);
  Result := S_OK;
end;


initialization
  assert(sizeof(TOleDBStatementParam) and (sizeof(Int64)-1)=0);
  TOleDBConnectionProperties.RegisterClassNameForDefinition;
  TOleDBOracleConnectionProperties.RegisterClassNameForDefinition;
  TOleDBMSOracleConnectionProperties.RegisterClassNameForDefinition;
  TOleDBMSSQLConnectionProperties.RegisterClassNameForDefinition;

Changes to SynopseCommit.inc.

1
'1.18.2017'
|
1
'1.18.2018'