Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: | {2018} included and fixed lasted mpv's patches for SynDB |
---|---|
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
bb7e33b0aaab64c2055103b41076da5b |
User & Date: | ab 2015-11-02 10:21:12 |
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 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'
|