Browse Source

* Merging revisions r42273,r42276,r42277,r42278,r42279,r42285,r42295,r42296,r42297,r42298,r42298,r42299,r42300 from trunk:
------------------------------------------------------------------------
r42273 | michael | 2019-06-23 00:38:02 +0200 (Sun, 23 Jun 2019) | 1 line

* Fix setusessl
------------------------------------------------------------------------
r42276 | lacak | 2019-06-23 20:02:38 +0200 (Sun, 23 Jun 2019) | 1 line

fcl-db: sqldb: add connection charset aliases "win1250" (Firebird) and "cp1250" (MySQL) (although unicode is preferred nowadays and these SBCS are just a remnant of the past)
------------------------------------------------------------------------
r42277 | michael | 2019-06-24 09:18:34 +0200 (Mon, 24 Jun 2019) | 1 line

* Correctly handle use in restmodule: use pathinfo to configure dispatcher
------------------------------------------------------------------------
r42278 | michael | 2019-06-24 09:22:02 +0200 (Mon, 24 Jun 2019) | 1 line

* Smarter handling of DispatchOptions options
------------------------------------------------------------------------
r42279 | michael | 2019-06-24 09:51:45 +0200 (Mon, 24 Jun 2019) | 1 line

* Allow using of connection charset if none is specified at DB level (bug ID 35755)
------------------------------------------------------------------------
r42285 | michael | 2019-06-26 10:34:47 +0200 (Wed, 26 Jun 2019) | 1 line

* Allow generators in sqlite in 3.0.4
------------------------------------------------------------------------
r42295 | michael | 2019-06-27 13:33:26 +0200 (Thu, 27 Jun 2019) | 1 line

* Better CORS handling: return origin if available and allowed domains not set (* will prohibit credentials)
------------------------------------------------------------------------
r42296 | michael | 2019-06-27 13:37:38 +0200 (Thu, 27 Jun 2019) | 1 line

* Better CORS handling: return origin if available and allowed domains not set (fix compilation)
------------------------------------------------------------------------
r42297 | michael | 2019-06-27 14:42:34 +0200 (Thu, 27 Jun 2019) | 1 line

* Better CORS handling: return origin if available and allowed domains not set (activate using option)
------------------------------------------------------------------------
r42298 | michael | 2019-06-27 18:13:15 +0200 (Thu, 27 Jun 2019) | 1 line

* Fix ISODateTime constant, trailing quote
------------------------------------------------------------------------
r42298 | michael | 2019-06-27 18:13:15 +0200 (Thu, 27 Jun 2019) | 1 line

* Fix ISODateTime constant, trailing quote
------------------------------------------------------------------------
r42299 | michael | 2019-06-27 18:13:55 +0200 (Thu, 27 Jun 2019) | 1 line

* Better code documentation for processql, add %OPTIONALWHERE%
------------------------------------------------------------------------
r42300 | michael | 2019-06-27 18:14:40 +0200 (Thu, 27 Jun 2019) | 1 line

* Some fixes in parameter handling
------------------------------------------------------------------------

git-svn-id: branches/fixes_3_2@42432 -

michael 6 years ago
parent
commit
02d9b6f18d

+ 4 - 1
packages/fcl-db/src/sqldb/interbase/ibconnection.pp

@@ -61,6 +61,7 @@ type
     FDatabaseInfo          : TDatabaseInfo;
     FDatabaseInfo          : TDatabaseInfo;
     FDialect               : integer;
     FDialect               : integer;
     FBlobSegmentSize       : word; //required for backward compatibilty; not used
     FBlobSegmentSize       : word; //required for backward compatibilty; not used
+    FUseConnectionCharSetIfNone: Boolean;
 
 
     procedure ConnectFB;
     procedure ConnectFB;
 
 
@@ -132,6 +133,7 @@ type
     property Params;
     property Params;
     property OnLogin;
     property OnLogin;
     Property Port stored false;
     Property Port stored false;
+    Property UseConnectionCharSetIfNone : Boolean Read FUseConnectionCharSetIfNone Write FUseConnectionCharSetIfNone;
   end;
   end;
   
   
   { TIBConnectionDef }
   { TIBConnectionDef }
@@ -988,7 +990,8 @@ begin
         TransType, TransLen, TransPrec);
         TransType, TransLen, TransPrec);
 
 
       // [var]char or blob column character set NONE or OCTETS overrides connection charset
       // [var]char or blob column character set NONE or OCTETS overrides connection charset
-      if ((TransType in [ftString, ftFixedChar]) and (PSQLVar^.sqlsubtype and $FF in [CS_NONE,CS_BINARY])) or
+      if (((TransType in [ftString, ftFixedChar]) and (PSQLVar^.sqlsubtype and $FF in [CS_NONE,CS_BINARY])) and not UseConnectionCharSetIfNone)
+         or
          ((TransType = ftMemo) and (PSQLVar^.relname_length>0) and (PSQLVar^.sqlname_length>0) and (GetBlobCharset(@PSQLVar^.relname,@PSQLVar^.sqlname) in [CS_NONE,CS_BINARY])) then
          ((TransType = ftMemo) and (PSQLVar^.relname_length>0) and (PSQLVar^.sqlname_length>0) and (GetBlobCharset(@PSQLVar^.relname,@PSQLVar^.sqlname) in [CS_NONE,CS_BINARY])) then
         FieldDefs.Add(PSQLVar^.AliasName, TransType, TransLen, TransPrec, (PSQLVar^.sqltype and 1)=0, False, i+1, CP_NONE)
         FieldDefs.Add(PSQLVar^.AliasName, TransType, TransLen, TransPrec, (PSQLVar^.sqltype and 1)=0, False, i+1, CP_NONE)
       else
       else

+ 2 - 0
packages/fcl-db/src/sqldb/sqldb.pp

@@ -1246,6 +1246,8 @@ begin
       FCodePage := CP_UTF8;
       FCodePage := CP_UTF8;
     'win1250','cp1250':
     'win1250','cp1250':
       FCodePage := 1250;
       FCodePage := 1250;
+    'win1251','cp1251':
+      FCodePage := 1251;
     'win1252','cp1252','latin1','iso8859_1':
     'win1252','cp1252','latin1','iso8859_1':
       FCodePage := 1252;
       FCodePage := 1252;
     else
     else

+ 1 - 1
packages/fcl-web/src/base/custhttpapp.pp

@@ -197,7 +197,7 @@ end;
 
 
 function TCustomHTTPApplication.GetUseSSL: Boolean;
 function TCustomHTTPApplication.GetUseSSL: Boolean;
 begin
 begin
-
+  Result:=HTTPHandler.UseSSL;
 end;
 end;
 
 
 procedure TCustomHTTPApplication.SetHostName(AValue: String);
 procedure TCustomHTTPApplication.SetHostName(AValue: String);

+ 74 - 8
packages/fcl-web/src/restbridge/sqldbrestbridge.pp

@@ -27,7 +27,8 @@ Type
                            rdoCustomView,          // Expose custom view /customview
                            rdoCustomView,          // Expose custom view /customview
                            rdoHandleCORS,          // Handle CORS requests
                            rdoHandleCORS,          // Handle CORS requests
                            rdoAccessCheckNeedsDB,  // Authenticate after connection to database was made.
                            rdoAccessCheckNeedsDB,  // Authenticate after connection to database was made.
-                           rdoConnectionResource   // Enable connection managament through /_connection[/:Conn] resource
+                           rdoConnectionResource,   // Enable connection managament through /_connection[/:Conn] resource
+                           rdoEmptyCORSDomainToOrigin // if CORSAllowedOrigins is empty CORS requests will mirror Origin instead of *
                            // rdoServerInfo        // Enable querying server info through /_serverinfo  resource
                            // rdoServerInfo        // Enable querying server info through /_serverinfo  resource
                            );
                            );
 
 
@@ -308,7 +309,7 @@ Type
     // General HTTP handling
     // General HTTP handling
     procedure DoRegisterRoutes; virtual;
     procedure DoRegisterRoutes; virtual;
     procedure DoHandleEvent(IsBefore : Boolean;IO: TRestIO); virtual;
     procedure DoHandleEvent(IsBefore : Boolean;IO: TRestIO); virtual;
-    function ResolvedCORSAllowedOrigins: String; virtual;
+    function ResolvedCORSAllowedOrigins(aRequest: TRequest): String; virtual;
     procedure HandleCORSRequest(aConnection: TSQLDBRestConnection; IO: TRestIO); virtual;
     procedure HandleCORSRequest(aConnection: TSQLDBRestConnection; IO: TRestIO); virtual;
     procedure HandleResourceRequest(aConnection : TSQLDBRestConnection; IO: TRestIO); virtual;
     procedure HandleResourceRequest(aConnection : TSQLDBRestConnection; IO: TRestIO); virtual;
     procedure DoHandleRequest(IO: TRestIO); virtual;
     procedure DoHandleRequest(IO: TRestIO); virtual;
@@ -322,6 +323,7 @@ Type
     procedure HandleMetadataRequest(aRequest : TRequest; aResponse : TResponse);
     procedure HandleMetadataRequest(aRequest : TRequest; aResponse : TResponse);
     procedure HandleConnRequest(aRequest : TRequest; aResponse : TResponse);
     procedure HandleConnRequest(aRequest : TRequest; aResponse : TResponse);
     procedure HandleRequest(aRequest : TRequest; aResponse : TResponse);
     procedure HandleRequest(aRequest : TRequest; aResponse : TResponse);
+    Procedure VerifyPathInfo(aRequest : TRequest);
     Function ExposeDatabase(Const aType,aHostName,aDatabaseName,aUserName,aPassword : String; aTables : Array of String; aMinFieldOpts : TRestFieldOptions = []) : TSQLDBRestConnection;
     Function ExposeDatabase(Const aType,aHostName,aDatabaseName,aUserName,aPassword : String; aTables : Array of String; aMinFieldOpts : TRestFieldOptions = []) : TSQLDBRestConnection;
     Function ExposeDatabase(Const aType,aHostName,aDatabaseName,aUserName,aPassword : String; aTables : TStrings = nil; aMinFieldOpts : TRestFieldOptions = []) : TSQLDBRestConnection;
     Function ExposeDatabase(Const aType,aHostName,aDatabaseName,aUserName,aPassword : String; aTables : TStrings = nil; aMinFieldOpts : TRestFieldOptions = []) : TSQLDBRestConnection;
     Function ExposeConnection(aOwner : TComponent; Const aConnection : TSQLDBRestConnection; aTables : TStrings = nil; aMinFieldOpts : TRestFieldOptions = []) : TSQLDBRestSchema;
     Function ExposeConnection(aOwner : TComponent; Const aConnection : TSQLDBRestConnection; aTables : TStrings = nil; aMinFieldOpts : TRestFieldOptions = []) : TSQLDBRestSchema;
@@ -409,7 +411,7 @@ Const
 
 
 implementation
 implementation
 
 
-uses fpjsonrtti, DateUtils, bufdataset, sqldbrestjson, sqldbrestconst;
+uses uriparser, fpjsonrtti, DateUtils, bufdataset, sqldbrestjson, sqldbrestconst;
 
 
 Type
 Type
 
 
@@ -492,9 +494,16 @@ end;
 
 
 procedure TSQLDBRestDispatcher.SetDispatchOptions(AValue: TRestDispatcherOptions);
 procedure TSQLDBRestDispatcher.SetDispatchOptions(AValue: TRestDispatcherOptions);
 
 
+Var
+  DeleteConnection : Boolean;
+
 begin
 begin
+  DeleteConnection:=(rdoConnectionInURL in FDispatchOptions) and Not (rdoConnectionInURL in aValue);
   if (rdoConnectionResource in aValue) then
   if (rdoConnectionResource in aValue) then
-    Include(aValue,rdoConnectionInURL);
+    if DeleteConnection then // if user disables rdoConnectionInURL, we disable rdoConnectionResource.
+      exclude(aValue,rdoConnectionResource)
+    else // else we include rdoConnectionInURL...
+      Include(aValue,rdoConnectionInURL);
   if FDispatchOptions=AValue then Exit;
   if FDispatchOptions=AValue then Exit;
   FDispatchOptions:=AValue;
   FDispatchOptions:=AValue;
 end;
 end;
@@ -1617,12 +1626,33 @@ begin
     end
     end
 end;
 end;
 
 
-function TSQLDBRestDispatcher.ResolvedCORSAllowedOrigins: String;
+function TSQLDBRestDispatcher.ResolvedCORSAllowedOrigins(aRequest : TRequest): String;
+
+Var
+  URl : String;
+  uri : TURI;
 
 
 begin
 begin
   Result:=FCORSAllowedOrigins;
   Result:=FCORSAllowedOrigins;
   if Result='' then
   if Result='' then
-     Result:='*';
+    begin
+    // Sent with CORS request
+    Result:=aRequest.GetCustomHeader('Origin');
+    if (Result='') and (rdoEmptyCORSDomainToOrigin in DispatchOptions) then
+      begin
+      // Fallback
+      URL:=aRequest.Referer;
+      if (URL<>'') then
+        begin
+        uri:=ParseURI(URL,'http',0);
+        Result:=Format('%s://%s',[URI.Protocol,URI.Host]);
+        if (URI.Port<>0) then
+          Result:=Result+':'+IntToStr(URI.Port);
+        end;
+      end;
+    end;
+  if Result='' then
+    Result:='*';
 end;
 end;
 
 
 procedure TSQLDBRestDispatcher.HandleCORSRequest(aConnection : TSQLDBRestConnection; IO : TRestIO);
 procedure TSQLDBRestDispatcher.HandleCORSRequest(aConnection : TSQLDBRestConnection; IO : TRestIO);
@@ -1644,7 +1674,7 @@ begin
     end
     end
   else
   else
     begin
     begin
-    IO.Response.SetCustomHeader('Access-Control-Allow-Origin',ResolvedCORSAllowedOrigins);
+    IO.Response.SetCustomHeader('Access-Control-Allow-Origin',ResolvedCORSAllowedOrigins(IO.Request));
     S:=IO.Resource.GetHTTPAllow;
     S:=IO.Resource.GetHTTPAllow;
     IO.Response.SetCustomHeader('Access-Control-Allow-Methods',S);
     IO.Response.SetCustomHeader('Access-Control-Allow-Methods',S);
     IO.Response.SetCustomHeader('Access-Control-Allow-Headers','x-requested-with, content-type, authorization');
     IO.Response.SetCustomHeader('Access-Control-Allow-Headers','x-requested-with, content-type, authorization');
@@ -1684,7 +1714,7 @@ begin
         Conn.OnLog:[email protected];
         Conn.OnLog:[email protected];
         end;
         end;
       if (rdoHandleCORS in DispatchOptions) then
       if (rdoHandleCORS in DispatchOptions) then
-        IO.Response.SetCustomHeader('Access-Control-Allow-Origin',ResolvedCORSAllowedOrigins);
+        IO.Response.SetCustomHeader('Access-Control-Allow-Origin',ResolvedCORSAllowedOrigins(IO.Request));
       if not AuthenticateRequest(IO,True) then
       if not AuthenticateRequest(IO,True) then
         exit;
         exit;
       if Not CheckResourceAccess(IO) then
       if Not CheckResourceAccess(IO) then
@@ -2002,6 +2032,42 @@ begin
   end;
   end;
 end;
 end;
 
 
+procedure TSQLDBRestDispatcher.VerifyPathInfo(aRequest: TRequest);
+Var
+  Full,Path : String;
+  BasePaths : TStringArray;
+  I : Integer;
+
+begin
+  // Check & discard basepath parts of the URL
+  Path:=aRequest.GetNextPathInfo;
+  Full:=BasePath;
+  BasePaths:=Full.Split('/',TStringSplitOptions.ExcludeEmpty);
+  I:=0;
+  While (I<Length(BasePaths)) and SameText(Path,BasePaths[i]) do
+    begin
+    inc(I);
+    Path:=aRequest.GetNextPathInfo;
+    end;
+  if (I<Length(BasePaths)) then
+    Raise ESQLDBRest.Create(400,'NOT FOUND');
+  // Path1 is now either connection or resource
+  if (rdoConnectionInURL in DispatchOptions) then
+    begin
+    // Both /metadata and /connection/metadata are possible
+    if not ((rdoExposeMetadata in DispatchOptions) and SameText(Path,Strings.getRestString(rpMetadataResourceName))) then
+      begin
+      aRequest.RouteParams['connection']:=Path;
+      Path:=aRequest.GetNextPathInfo;
+      end;
+    end;
+  aRequest.RouteParams['resource']:=Path;
+  // Next part is ID
+  Path:=aRequest.GetNextPathInfo;
+  if (Path<>'') then
+    aRequest.RouteParams['ID']:=Path;
+end;
+
 function TSQLDBRestDispatcher.ExposeDatabase(const aType, aHostName, aDatabaseName, aUserName, aPassword: String;
 function TSQLDBRestDispatcher.ExposeDatabase(const aType, aHostName, aDatabaseName, aUserName, aPassword: String;
   aTables: array of String; aMinFieldOpts: TRestFieldOptions): TSQLDBRestConnection;
   aTables: array of String; aMinFieldOpts: TRestFieldOptions): TSQLDBRestConnection;
 
 

+ 1 - 1
packages/fcl-web/src/restbridge/sqldbrestconst.pp

@@ -52,7 +52,7 @@ Resourcestring
 
 
 Const
 Const
   DefaultAuthenticationRealm = 'REST API Server';
   DefaultAuthenticationRealm = 'REST API Server';
-  ISODateTimeFormat = 'YYYY"-"mm"-"dd"T"hh":"nn":"ss"';
+  ISODateTimeFormat = 'YYYY"-"mm"-"dd"T"hh":"nn":"ss';
   ISODateFormat = ISODateTimeFormat;
   ISODateFormat = ISODateTimeFormat;
   ISOTimeFormat = '"0000-00-00T"hh":"nn":"ss"';
   ISOTimeFormat = '"0000-00-00T"hh":"nn":"ss"';
 
 

+ 48 - 20
packages/fcl-web/src/restbridge/sqldbrestdata.pp

@@ -64,7 +64,8 @@ Type
     function FindFieldForParam(aOperation: TRestOperation; P: TParam): TSQLDBRestField; virtual;
     function FindFieldForParam(aOperation: TRestOperation; P: TParam): TSQLDBRestField; virtual;
     function BuildFieldList(ForceAll : Boolean): TRestFieldPairArray; virtual;
     function BuildFieldList(ForceAll : Boolean): TRestFieldPairArray; virtual;
     function CreateQuery(aSQL: String): TSQLQuery; virtual;
     function CreateQuery(aSQL: String): TSQLQuery; virtual;
-    procedure FillParams(aOperation: TRestOperation; aQuery: TSQLQuery; FilteredFields: TRestFilterPairArray); virtual;
+    procedure FillParams(aOperation: TRestOperation; aParams: TParams;
+      FilteredFields: TRestFilterPairArray); virtual;
     function GetDatasetForResource(aFieldList: TRestFieldPairArray; Singleton : Boolean): TDataset; virtual;
     function GetDatasetForResource(aFieldList: TRestFieldPairArray; Singleton : Boolean): TDataset; virtual;
     function GetOrderByFieldArray: TRestFieldOrderPairArray;
     function GetOrderByFieldArray: TRestFieldOrderPairArray;
     function GetOrderBy: UTF8String;virtual;
     function GetOrderBy: UTF8String;virtual;
@@ -388,7 +389,12 @@ end;
 procedure TSQLDBRestDBHandler.SetParamFromData(P: TParam; F: TSQLDBRestField;
 procedure TSQLDBRestDBHandler.SetParamFromData(P: TParam; F: TSQLDBRestField;
   D: TJSONData);
   D: TJSONData);
 
 
+Var
+  S : String;
+
 begin
 begin
+  if Assigned(D) then
+    S:=D.AsString;
   if not Assigned(D) then
   if not Assigned(D) then
     P.Clear
     P.Clear
   else if Assigned(F) then
   else if Assigned(F) then
@@ -434,7 +440,7 @@ begin
     Result:=FResource.Fields.FindByFieldName(N);
     Result:=FResource.Fields.FindByFieldName(N);
 end;
 end;
 
 
-procedure TSQLDBRestDBHandler.FillParams(aOperation : TRestOperation; aQuery: TSQLQuery;FilteredFields : TRestFilterPairArray);
+procedure TSQLDBRestDBHandler.FillParams(aOperation : TRestOperation; aParams: TParams;FilteredFields : TRestFilterPairArray);
 
 
 Var
 Var
   I : Integer;
   I : Integer;
@@ -452,19 +458,21 @@ begin
     F:=FF.Field;
     F:=FF.Field;
     if FF.Operation<>rfNull then
     if FF.Operation<>rfNull then
       begin
       begin
-      P:=aQuery.Params.FindParam(FilterParamPrefix[FF.Operation]+F.FieldName);
-      if not Assigned(P) then
-        Raise ESQLDBRest.CreateFmt(IO.RestStatuses.GetStatusCode(rsError),SErrFilterParamNotFound,[F.PublicName]);
-      if Assigned(FF.ValueParam) then
-        P.Value:=FF.ValueParam.Value
-      else
+      P:=aParams.FindParam(FilterParamPrefix[FF.Operation]+F.FieldName);
+      // If there is no %where% macro, the parameter can be absent
+      if Assigned(P) then
         begin
         begin
-        D:=TJSONString.Create(FF.Value);
-        try
-          SetParamFromData(P,F,D)
-        finally
-          D.Free;
-        end;
+        if Assigned(FF.ValueParam) then
+          P.Value:=FF.ValueParam.Value
+        else
+          begin
+          D:=TJSONString.Create(FF.Value);
+          try
+            SetParamFromData(P,F,D)
+          finally
+            D.Free;
+          end;
+          end;
         end;
         end;
       end;
       end;
     end;
     end;
@@ -477,9 +485,9 @@ begin
   else
   else
     Sources:=AllVariableSources;
     Sources:=AllVariableSources;
   end;
   end;
-  For I:=0 to aQuery.Params.Count-1 do
+  For I:=0 to aParams.Count-1 do
     begin
     begin
-    P:=aQuery.Params[i];
+    P:=aParams[i];
     if P.IsNull then
     if P.IsNull then
       try
       try
         D:=Nil;
         D:=Nil;
@@ -654,7 +662,7 @@ begin
   Q:=CreateQuery(SQL);
   Q:=CreateQuery(SQL);
   Try
   Try
     Q.UsePrimaryKeyAsKey:=False;
     Q.UsePrimaryKeyAsKey:=False;
-    FillParams(roGet,Q,WhereFilterList);
+    FillParams(roGet,Q.Params,WhereFilterList);
     Result:=Q;
     Result:=Q;
   except
   except
     Q.Free;
     Q.Free;
@@ -715,6 +723,23 @@ function TSQLDBRestDBHandler.GetGeneratorValue(const aGeneratorName: String
   ): Int64;
   ): Int64;
 
 
 begin
 begin
+{$IFDEF VER3_0_4}
+  // The 'get next value' SQL in 3.0.4 is wrong, so we need to do this sep
+  if (IO.Connection is TSQLConnector) and SameText((IO.Connection as TSQLConnector).ConnectorType,'Sqlite3') then
+    begin
+    With CreateQuery('SELECT seq+1 FROM sqlite_sequence WHERE name=:aName') do
+      Try
+        ParamByName('aName').AsString:=aGeneratorName;
+        Open;
+        if (EOF and BOF) then
+          DatabaseErrorFmt('Generator %s does not exist',[aGeneratorName]);
+        Result:=Fields[0].asLargeint;
+      Finally
+        Free;
+      end;
+    end
+  else
+{$ENDIF}
   Result:=IO.Connection.GetNextValue(aGeneratorName,1);
   Result:=IO.Connection.GetNextValue(aGeneratorName,1);
 end;
 end;
 
 
@@ -870,7 +895,7 @@ begin
   if not IO.RESTInput.SelectObject(0) then
   if not IO.RESTInput.SelectObject(0) then
     raise ESQLDBRest.Create(IO.RestStatuses.GetStatusCode(rsInvalidParam), SErrNoResourceDataFound);
     raise ESQLDBRest.Create(IO.RestStatuses.GetStatusCode(rsInvalidParam), SErrNoResourceDataFound);
   InsertNewRecord;
   InsertNewRecord;
-  // Now build response
+  // Now build response. We can imagine not doing a select again, and simply supply back the fields as sent...
   FieldList:=BuildFieldList(False);
   FieldList:=BuildFieldList(False);
   D:=GetDatasetForResource(FieldList,True);
   D:=GetDatasetForResource(FieldList,True);
   try
   try
@@ -887,6 +912,7 @@ procedure TSQLDBRestDBHandler.UpdateExistingRecord(OldData : TDataset);
 Var
 Var
   S : TSQLStatement;
   S : TSQLStatement;
   SQl : String;
   SQl : String;
+  WhereFilterList : TRestFilterPairArray;
 
 
 begin
 begin
   if (OldData=ExternalDataset) then
   if (OldData=ExternalDataset) then
@@ -902,13 +928,14 @@ begin
     end
     end
   else
   else
     begin
     begin
-    SQL:=FResource.GetResolvedSQl(skUpdate,'','','');
+    SQL:=FResource.GetResolvedSQl(skUpdate,GetIDWhere(WhereFilterList) ,'','');
     S:=TSQLStatement.Create(Self);
     S:=TSQLStatement.Create(Self);
     try
     try
       S.Database:=IO.Connection;
       S.Database:=IO.Connection;
       S.Transaction:=IO.Transaction;
       S.Transaction:=IO.Transaction;
       S.SQL.Text:=SQL;
       S.SQL.Text:=SQL;
       SetPostParams(S.Params,OldData.Fields);
       SetPostParams(S.Params,OldData.Fields);
+      FillParams(roGet,S.Params,WhereFilterList);
       // Give user a chance to look at it.
       // Give user a chance to look at it.
       FResource.CheckParams(io.RestContext,roPut,S.Params);
       FResource.CheckParams(io.RestContext,roPut,S.Params);
       S.Execute;
       S.Execute;
@@ -976,6 +1003,7 @@ begin
     // Now build response
     // Now build response
     if D<>ExternalDataset then
     if D<>ExternalDataset then
       begin;
       begin;
+      // Now build response. We can imagine not doing a select again, and simply supply back the fields as sent...
       FreeAndNil(D);
       FreeAndNil(D);
       D:=GetDatasetForResource(FieldList,True);
       D:=GetDatasetForResource(FieldList,True);
       FieldList:=BuildFieldList(False);
       FieldList:=BuildFieldList(False);
@@ -1026,7 +1054,7 @@ begin
     SQL:=FResource.GetResolvedSQl(skDelete,aWhere,'');
     SQL:=FResource.GetResolvedSQl(skDelete,aWhere,'');
     Q:=CreateQuery(SQL);
     Q:=CreateQuery(SQL);
     try
     try
-      FillParams(roDelete,Q,FilteredFields);
+      FillParams(roDelete,Q.Params,FilteredFields);
       Q.ExecSQL;
       Q.ExecSQL;
       if Q.RowsAffected<>1 then
       if Q.RowsAffected<>1 then
         DoNotFound;
         DoNotFound;

+ 15 - 1
packages/fcl-web/src/restbridge/sqldbrestmodule.pp

@@ -17,6 +17,7 @@ Type
     procedure SetDispatcher(AValue: TSQLDBRestDispatcher);
     procedure SetDispatcher(AValue: TSQLDBRestDispatcher);
   Protected
   Protected
     Procedure Notification(AComponent: TComponent; Operation: TOperation); override;
     Procedure Notification(AComponent: TComponent; Operation: TOperation); override;
+    procedure ConfigureDispatcherFromRequest(Disp: TSQLDBRestDispatcher; aRequest: TRequest); virtual;
     Function FindDispatcher : TSQLDBRestDispatcher; virtual;
     Function FindDispatcher : TSQLDBRestDispatcher; virtual;
   Public
   Public
     constructor Create(AOwner: TComponent); override;
     constructor Create(AOwner: TComponent); override;
@@ -28,7 +29,7 @@ Type
 
 
 implementation
 implementation
 
 
-uses sqldbrestconst;
+uses sqldbrestschema, sqldbrestconst;
 
 
 { TSQLDBRestModule }
 { TSQLDBRestModule }
 
 
@@ -39,7 +40,10 @@ begin
     FDispatcher.RemoveFreeNotification(Self);
     FDispatcher.RemoveFreeNotification(Self);
   FDispatcher:=AValue;
   FDispatcher:=AValue;
   if Assigned(Dispatcher) then
   if Assigned(Dispatcher) then
+    begin
+    FDispatcher.Active:=False;
     FDispatcher.FreeNotification(Self);
     FDispatcher.FreeNotification(Self);
+    end;
 end;
 end;
 
 
 procedure TSQLDBRestModule.Notification(AComponent: TComponent; Operation: TOperation);
 procedure TSQLDBRestModule.Notification(AComponent: TComponent; Operation: TOperation);
@@ -61,6 +65,12 @@ begin
   inherited Create(AOwner);
   inherited Create(AOwner);
 end;
 end;
 
 
+procedure TSQLDBRestModule.ConfigureDispatcherFromRequest(Disp : TSQLDBRestDispatcher; aRequest : TRequest);
+
+begin
+  Disp.VerifyPathInfo(aRequest);
+end;
+
 procedure TSQLDBRestModule.HandleRequest(ARequest: TRequest; AResponse: TResponse);
 procedure TSQLDBRestModule.HandleRequest(ARequest: TRequest; AResponse: TResponse);
 
 
 Var
 Var
@@ -69,7 +79,11 @@ Var
 begin
 begin
   Disp:=FindDispatcher;
   Disp:=FindDispatcher;
   If assigned(Disp) then
   If assigned(Disp) then
+    begin
+    Disp.Active:=False;
+    ConfigureDispatcherFromRequest(Disp,aRequest);
     Disp.HandleRequest(aRequest,aResponse)
     Disp.HandleRequest(aRequest,aResponse)
+    end
   else
   else
     Raise EHTTP.Create(SErrNoRESTDispatcher);
     Raise EHTTP.Create(SErrNoRESTDispatcher);
 end;
 end;

+ 14 - 1
packages/fcl-web/src/restbridge/sqldbrestschema.pp

@@ -1079,7 +1079,7 @@ Const
 
 
 Const
 Const
   Wheres = [flWhereKey];
   Wheres = [flWhereKey];
-  Colons = Wheres + [flInsertParams];
+  Colons = Wheres + [flInsertParams,flUpdate];
   UseEqual = Wheres+[flUpdate];
   UseEqual = Wheres+[flUpdate];
 
 
 Var
 Var
@@ -1178,16 +1178,29 @@ Var
 
 
 begin
 begin
   Result:=aSQL;
   Result:=aSQL;
+
+  // from tables %FULLWHERE%
   if (aWhere<>'') then
   if (aWhere<>'') then
     S:='WHERE '+aWhere
     S:='WHERE '+aWhere
   else
   else
     S:='';
     S:='';
   Result:=StringReplace(Result,'%FULLWHERE%',S,[rfReplaceAll]);
   Result:=StringReplace(Result,'%FULLWHERE%',S,[rfReplaceAll]);
+
+  // from tables WHERE %REQUIREDWHERE%
   if (aWhere<>'') then
   if (aWhere<>'') then
     S:=aWhere
     S:=aWhere
   else
   else
     S:='(1=0)';
     S:='(1=0)';
   Result:=StringReplace(Result,'%REQUIREDWHERE%',S,[rfReplaceAll]);
   Result:=StringReplace(Result,'%REQUIREDWHERE%',S,[rfReplaceAll]);
+
+  // from tables WHERE X=Y %OPTIONALWHERE%
+  if (aWhere<>'') then
+    S:='AND ('+aWhere+')'
+  else
+    S:='';
+  Result:=StringReplace(Result,'%OPTIONALWHERE%',S,[rfReplaceAll]);
+
+  // from tables WHERE X=Y AND %WHERE%
   if (aWhere<>'') then
   if (aWhere<>'') then
     S:='('+aWhere+')'
     S:='('+aWhere+')'
   else
   else