Browse Source

* Add support for select query parameters

Michaël Van Canneyt 2 years ago
parent
commit
a35f66a730

+ 138 - 24
packages/fcl-web/src/restbridge/sqldbrestbridge.pp

@@ -171,6 +171,7 @@ Type
     FLogOptions: TRestDispatcherLogOptions;
     FLogOptions: TRestDispatcherLogOptions;
     FMetadataResource : TSQLDBRestResource;
     FMetadataResource : TSQLDBRestResource;
     FMetadataDetailResource : TSQLDBRestResource;
     FMetadataDetailResource : TSQLDBRestResource;
+    FMetadataParametersResource : TSQLDBRestResource;
     FConnectionResource : TSQLDBRestResource;
     FConnectionResource : TSQLDBRestResource;
     FActive: Boolean;
     FActive: Boolean;
     FAfterDelete: TRestOperationEvent;
     FAfterDelete: TRestOperationEvent;
@@ -200,10 +201,12 @@ Type
     FSchemas: TSQLDBRestSchemaList;
     FSchemas: TSQLDBRestSchemaList;
     FListRoute: THTTPRoute;
     FListRoute: THTTPRoute;
     FItemRoute: THTTPRoute;
     FItemRoute: THTTPRoute;
+    FParamRoute: THTTPRoute;
     FConnectionsRoute: THTTPRoute;
     FConnectionsRoute: THTTPRoute;
     FConnectionItemRoute: THTTPRoute;
     FConnectionItemRoute: THTTPRoute;
     FMetadataRoute: THTTPRoute;
     FMetadataRoute: THTTPRoute;
     FMetadataItemRoute: THTTPRoute;
     FMetadataItemRoute: THTTPRoute;
+    FMetadataParameterRoute : THTTPRoute;
     FStatus: TRestStatusConfig;
     FStatus: TRestStatusConfig;
     FStrings: TRestStringsConfig;
     FStrings: TRestStringsConfig;
     FAfterDatabaseRead: TRestDatabaseEvent;
     FAfterDatabaseRead: TRestDatabaseEvent;
@@ -279,14 +282,18 @@ Type
     // Special resources for Metadata handling
     // Special resources for Metadata handling
     function CreateMetadataDataset(IO: TRestIO; AOwner: TComponent): TDataset; virtual;
     function CreateMetadataDataset(IO: TRestIO; AOwner: TComponent): TDataset; virtual;
     function CreateMetadataDetailDataset(IO: TRestIO; Const aResourceName : String; AOwner: TComponent): TDataset; virtual;
     function CreateMetadataDetailDataset(IO: TRestIO; Const aResourceName : String; AOwner: TComponent): TDataset; virtual;
+    function CreateMetadataParameterDataset(IO: TRestIO; Const aResourceName : String; AOwner: TComponent): TDataset; virtual;
     function CreateConnectionDataset(IO: TRestIO; AOwner: TComponent): TDataset; virtual;
     function CreateConnectionDataset(IO: TRestIO; AOwner: TComponent): TDataset; virtual;
+    // Resource definitions for metadata
     function CreateMetadataDetailResource: TSQLDBRestResource;  virtual;
     function CreateMetadataDetailResource: TSQLDBRestResource;  virtual;
     function CreateMetadataResource: TSQLDBRestResource; virtual;
     function CreateMetadataResource: TSQLDBRestResource; virtual;
+    function CreateMetadataParameterResource: TSQLDBRestResource; virtual;
     Function CreateConnectionResource : TSQLDBRestResource; virtual;
     Function CreateConnectionResource : TSQLDBRestResource; virtual;
     // Custom view handling
     // Custom view handling
     function CreateCustomViewResource: TSQLDBRestResource; virtual;
     function CreateCustomViewResource: TSQLDBRestResource; virtual;
     function CreateCustomViewDataset(IO: TRestIO; const aSQL: String; AOwner: TComponent): TDataset;
     function CreateCustomViewDataset(IO: TRestIO; const aSQL: String; AOwner: TComponent): TDataset;
     procedure ResourceToDataset(R: TSQLDBRestResource; D: TDataset); virtual;
     procedure ResourceToDataset(R: TSQLDBRestResource; D: TDataset); virtual;
+    procedure ResourceParamsToDataset(R: TSQLDBRestResource; D: TDataset); virtual;
     procedure SchemasToDataset(D: TDataset);virtual;
     procedure SchemasToDataset(D: TDataset);virtual;
     // General HTTP handling
     // General HTTP handling
     procedure DoRegisterRoutes; virtual;
     procedure DoRegisterRoutes; virtual;
@@ -302,6 +309,7 @@ Type
     Destructor Destroy; override;
     Destructor Destroy; override;
     procedure RegisterRoutes;
     procedure RegisterRoutes;
     procedure UnRegisterRoutes;
     procedure UnRegisterRoutes;
+    procedure HandleMetadataParameterRequest(aRequest : TRequest; aResponse : TResponse);
     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);
@@ -405,18 +413,10 @@ Const
 
 
 implementation
 implementation
 
 
-uses uriparser, fpjsonrtti, DateUtils, bufdataset, sqldbrestjson, sqldbrestconst;
+uses typinfo,uriparser, fpjsonrtti, DateUtils, bufdataset, sqldbrestjson, sqldbrestconst;
 
 
 Type
 Type
 
 
-  { TRestBufDataset }
-
-  TRestBufDataset = class (TBufDataset)
-  protected
-    procedure LoadBlobIntoBuffer(FieldDef: TFieldDef; ABlobBuf: PBufBlobField); override;
-  end;
-
-
   { TSchemaFreeNotifier }
   { TSchemaFreeNotifier }
 
 
   TSchemaFreeNotifier = Class(TComponent)
   TSchemaFreeNotifier = Class(TComponent)
@@ -431,17 +431,6 @@ Type
     Procedure Notification(AComponent: TComponent; Operation: TOperation); override;
     Procedure Notification(AComponent: TComponent; Operation: TOperation); override;
   end;
   end;
 
 
-{ TRestBufDataset }
-
-procedure TRestBufDataset.LoadBlobIntoBuffer(FieldDef: TFieldDef; ABlobBuf: PBufBlobField);
-begin
-  If (FieldDef=Nil) or (aBlobBuf=Nil) then
-    exit;
-end;
-
-
-
-
 
 
 { TConnectionFreeNotifier }
 { TConnectionFreeNotifier }
 
 
@@ -712,17 +701,18 @@ begin
       LogMsg:='User: '+UN+LogMsg;
       LogMsg:='User: '+UN+LogMsg;
     DoLog(rtloHTTP,Nil,LogMsg);
     DoLog(rtloHTTP,Nil,LogMsg);
     end;
     end;
-  aRequest.RouteParams['resource']:=Strings.MetadataResourceName;
+  aRequest.RouteParams['resource']:='_'+Strings.MetadataResourceName;
   HandleRequest(aRequest,aResponse);
   HandleRequest(aRequest,aResponse);
 end;
 end;
 
 
 procedure TSQLDBRestDispatcher.DoRegisterRoutes;
 procedure TSQLDBRestDispatcher.DoRegisterRoutes;
 
 
 Var
 Var
-  Res,C : UTF8String;
+  Res,P,C : UTF8String;
 
 
 begin
 begin
   Res:=IncludeHTTPPathDelimiter(BasePath);
   Res:=IncludeHTTPPathDelimiter(BasePath);
+  P:=Strings.GetRestString(rpParametersRoutePart);
   if (rdoConnectionResource in DispatchOptions) then
   if (rdoConnectionResource in DispatchOptions) then
     begin
     begin
     C:=Strings.GetRestString(rpConnectionResourceName);
     C:=Strings.GetRestString(rpConnectionResourceName);
@@ -738,12 +728,14 @@ begin
       begin
       begin
       C:=Strings.GetRestString(rpMetadataResourceName);
       C:=Strings.GetRestString(rpMetadataResourceName);
       FMetadataRoute:=HTTPRouter.RegisterRoute(res+C,@HandleMetaDataRequest);
       FMetadataRoute:=HTTPRouter.RegisterRoute(res+C,@HandleMetaDataRequest);
+      FMetadataParameterRoute:=HTTPRouter.RegisterRoute(res+C+'/:ResourceName/'+P,@HandleMetadataParameterRequest);
       FMetadataItemRoute:=HTTPRouter.RegisterRoute(res+C+'/:id',@HandleMetaDataRequest);
       FMetadataItemRoute:=HTTPRouter.RegisterRoute(res+C+'/:id',@HandleMetaDataRequest);
       end;
       end;
     Res:=Res+':connection/';
     Res:=Res+':connection/';
     end;
     end;
   Res:=Res+':resource';
   Res:=Res+':resource';
   FListRoute:=HTTPRouter.RegisterRoute(res,@HandleRequest);
   FListRoute:=HTTPRouter.RegisterRoute(res,@HandleRequest);
+  FParamRoute:=HTTPRouter.RegisterRoute(Res+'/:ResourceName/'+P,@HandleMetadataParameterRequest);
   FItemRoute:=HTTPRouter.RegisterRoute(Res+'/:id',@HandleRequest);
   FItemRoute:=HTTPRouter.RegisterRoute(Res+'/:id',@HandleRequest);
 end;
 end;
 
 
@@ -930,6 +922,7 @@ begin
   FreeAndNil(FCustomViewResource);
   FreeAndNil(FCustomViewResource);
   FreeAndNil(FMetadataResource);
   FreeAndNil(FMetadataResource);
   FreeAndNil(FMetadataDetailResource);
   FreeAndNil(FMetadataDetailResource);
+  FreeAndNil(FMetadataParametersResource);
   FreeAndNil(FConnectionResource);
   FreeAndNil(FConnectionResource);
   FreeAndNil(FSchemas);
   FreeAndNil(FSchemas);
   FreeAndNil(FConnections);
   FreeAndNil(FConnections);
@@ -1001,6 +994,23 @@ begin
       end;
       end;
 end;
 end;
 
 
+function TSQLDBRestDispatcher.CreateMetadataParameterResource: TSQLDBRestResource;
+Var
+  O : TRestFieldOption;
+  S : String;
+
+begin
+  Result:=TSQLDBRestResource.Create(Nil);
+  Result.ResourceName:='metaDataParameters';
+  if rdoHandleCORS in DispatchOptions then
+    Result.AllowedOperations:=[roGet,roOptions,roHead]
+  else
+    Result.AllowedOperations:=[roGet,roHead];
+  Result.Fields.AddField('name',rftString,[]).MaxLen:=255;
+  Result.Fields.AddField('type',rftString,[]).MaxLen:=20;
+  Result.Fields.AddField('default',rftString,[]).MaxLen:=1025;
+end;
+
 function TSQLDBRestDispatcher.CreateConnectionResource: TSQLDBRestResource;
 function TSQLDBRestDispatcher.CreateConnectionResource: TSQLDBRestResource;
 Var
 Var
   Def : TRestFieldOptions;
   Def : TRestFieldOptions;
@@ -1068,6 +1078,13 @@ function TSQLDBRestDispatcher.FindSpecialResource(IO : TRestIO; aResource: UTF8S
             and SameText(aResource,Strings.GetRestString(rpMetaDataResourceName));
             and SameText(aResource,Strings.GetRestString(rpMetaDataResourceName));
   end;
   end;
 
 
+  Function IsMetadataParams : Boolean; // inline;
+
+  begin
+    Result:=(rdoExposeMetadata in DispatchOptions)
+            and SameText(aResource,Strings.MetadataParametersName);
+  end;
+
   Function IsConnection : Boolean;inline;
   Function IsConnection : Boolean;inline;
 
 
   begin
   begin
@@ -1092,6 +1109,15 @@ begin
       FConnectionResource:=CreateConnectionResource;
       FConnectionResource:=CreateConnectionResource;
     Result:=FConnectionResource;
     Result:=FConnectionResource;
     end
     end
+  else If isMetadataParams then
+    begin
+    if (IO.GetVariable('ResourceName',N,[vsRoute,vsQuery])<>vsNone) then
+      begin
+      if FMetadataParametersResource=Nil  then
+        FMetadataParametersResource:=CreateMetadataParameterResource;
+      Result:=FMetadataParametersResource;
+      end
+    end
   else If isMetadata then
   else If isMetadata then
     if (IO.GetVariable('ID',N,[vsRoute,vsQuery])=vsNone) then
     if (IO.GetVariable('ID',N,[vsRoute,vsQuery])=vsNone) then
       begin
       begin
@@ -1334,7 +1360,7 @@ begin
       R:=Nil;
       R:=Nil;
     end;
     end;
   If Assigned(Evt) then
   If Assigned(Evt) then
-    Evt(Self,IO.Operation,IO.RestContext,IO.Resource);
+    Evt(Self,IO.OPeration,IO.RestContext,IO.Resource);
   If Assigned(ResEvt) then
   If Assigned(ResEvt) then
     ResEvt(Self,IO.Operation,IO.RestContext,IO.Resource);
     ResEvt(Self,IO.Operation,IO.RestContext,IO.Resource);
   If Assigned(R) then
   If Assigned(R) then
@@ -1409,6 +1435,7 @@ begin
   if not Result then exit;
   if not Result then exit;
   Result:=(aResource=FMetadataResource) or
   Result:=(aResource=FMetadataResource) or
           (aResource=FMetadataDetailResource) or
           (aResource=FMetadataDetailResource) or
+          (aResource=FMetadataParametersResource) or
           (aResource=FConnectionResource) or
           (aResource=FConnectionResource) or
           (aResource=FCustomViewResource);
           (aResource=FCustomViewResource);
 end;
 end;
@@ -1529,6 +1556,29 @@ begin
     end;
     end;
 end;
 end;
 
 
+procedure TSQLDBRestDispatcher.ResourceParamsToDataset(R: TSQLDBRestResource;
+  D: TDataset);
+Var
+  P : TSQLDBRestParam;
+  O : TRestFieldOption;
+  I : Integer;
+  FName,FType,fDefault : TField;
+  FOptions : Array[TRestFieldOption] of TField;
+
+begin
+  FName:=D.FieldByName('name');
+  FType:=D.FieldByName('type');
+  FDefault:=D.FieldByName('default');
+  For P in R.Parameters do
+    begin
+    D.Append;
+    FName.AsString:=P.Name;
+    FType.AsString:=GetEnumName(TypeInfo(TFieldType),Ord(P.DataType));
+    fDefault.AsString:=P.DefaultValue;
+    D.Post;
+    end;
+end;
+
 function TSQLDBRestDispatcher.CreateMetadataDetailDataset(IO: TRestIO;
 function TSQLDBRestDispatcher.CreateMetadataDetailDataset(IO: TRestIO;
   const aResourceName: String; AOwner: TComponent): TDataset;
   const aResourceName: String; AOwner: TComponent): TDataset;
 
 
@@ -1555,7 +1605,35 @@ begin
       end;
       end;
     BD.CreateDataset;
     BD.CreateDataset;
     R:=FindRestResource(aResourceName);
     R:=FindRestResource(aResourceName);
-    ResourceToDataset(R,BD);
+    if assigned(R) then
+      ResourceToDataset(R,BD);
+    BD.First;
+  except
+    BD.Free;
+    Raise;
+  end;
+end;
+
+function TSQLDBRestDispatcher.CreateMetadataParameterDataset(IO: TRestIO;
+  const aResourceName: String; AOwner: TComponent): TDataset;
+Var
+  BD :  TRestBufDataset;
+  O : TRestFieldOption;
+  SO : String;
+  R : TSQLDBRestResource;
+
+begin
+  if IO=Nil then exit;
+  BD:=TRestBufDataset.Create(aOwner);
+  try
+    Result:=BD;
+    Result.FieldDefs.Add('name',ftString,255,False);
+    Result.FieldDefs.Add('type',ftString,20,False);
+    Result.FieldDefs.Add('default',ftString,1024,false);
+    BD.CreateDataset;
+    R:=FindRestResource(aResourceName);
+    if Assigned(R) then
+      ResourceParamsToDataset(R,BD);
     BD.First;
     BD.First;
   except
   except
     BD.Free;
     BD.Free;
@@ -1777,6 +1855,12 @@ begin
       raise ESQLDBRest.Create(FStatus.GetStatusCode(rsError), SErrCouldNotFindResourceName); // Should never happen.
       raise ESQLDBRest.Create(FStatus.GetStatusCode(rsError), SErrCouldNotFindResourceName); // Should never happen.
     Result:=CreateMetadataDetailDataset(IO,RN,AOwner)
     Result:=CreateMetadataDetailDataset(IO,RN,AOwner)
     end
     end
+  else if (IO.Resource=FMetadataParametersResource) then
+    begin
+    if IO.GetVariable('ResourceName',RN,[vsRoute,vsQuery])=vsNone then
+      raise ESQLDBRest.Create(FStatus.GetStatusCode(rsError), SErrCouldNotFindResourceName); // Should never happen.
+    Result:=CreateMetadataParameterDataset(IO,RN,AOwner)
+    end
   else   if (IO.Resource=FCustomViewResource) then
   else   if (IO.Resource=FCustomViewResource) then
     begin
     begin
     if IO.GetVariable(FStrings.GetRestString(rpCustomViewSQLParam),RN,[vsRoute,vsQuery])=vsNone then
     if IO.GetVariable(FStrings.GetRestString(rpCustomViewSQLParam),RN,[vsRoute,vsQuery])=vsNone then
@@ -2082,9 +2166,37 @@ begin
   Un(FConnectionItemRoute);
   Un(FConnectionItemRoute);
   Un(FConnectionsRoute);
   Un(FConnectionsRoute);
   Un(FMetadataItemRoute);
   Un(FMetadataItemRoute);
+  Un(FMetadataParameterRoute);
   Un(FMetadataRoute);
   Un(FMetadataRoute);
 end;
 end;
 
 
+procedure TSQLDBRestDispatcher.HandleMetadataParameterRequest(
+  aRequest: TRequest; aResponse: TResponse);
+Var
+  LogMsg,UN : UTF8String;
+
+begin
+  if MustLog(rtloHTTP) then
+    begin
+    LogMsg:='';
+    With aRequest do
+      begin
+      UN:=RemoteHost;
+      if (UN='') then
+        UN:=RemoteAddr;
+      if (UN<>'') then
+        LogMsg:='From: '+UN+'; ';
+      LogMsg:=LogMsg+'URL: '+URL;
+      end;
+    UN:=TRestBasicAuthenticator.ExtractUserName(aRequest);
+    if (UN<>'?') then
+      LogMsg:='User: '+UN+LogMsg;
+    DoLog(rtloHTTP,Nil,LogMsg);
+    end;
+  aRequest.RouteParams['resource']:=Strings.MetadataParametersName;
+  HandleRequest(aRequest,aResponse);
+end;
+
 
 
 procedure TSQLDBRestDispatcher.RegisterRoutes;
 procedure TSQLDBRestDispatcher.RegisterRoutes;
 begin
 begin
@@ -2596,6 +2708,8 @@ begin
   TSQLDBRestDispatcher.SetDBHandlerClass(TSQLDBRestDBHandler);
   TSQLDBRestDispatcher.SetDBHandlerClass(TSQLDBRestDBHandler);
   TSQLDBRestResource.DefaultFieldListClass:=TSQLDBRestFieldList;
   TSQLDBRestResource.DefaultFieldListClass:=TSQLDBRestFieldList;
   TSQLDBRestResource.DefaultFieldClass:=TSQLDBRestField;
   TSQLDBRestResource.DefaultFieldClass:=TSQLDBRestField;
+  TSQLDBRestResource.DefaultParameterListClass:=TSQLDBRestParameterList;
+  TSQLDBRestResource.DefaultParamClass:=TSQLDBRestParam;
 end;
 end;
 
 
 Initialization
 Initialization

+ 2 - 0
packages/fcl-web/src/restbridge/sqldbrestconst.pp

@@ -52,6 +52,8 @@ Resourcestring
   SErrMissingInputFields = 'Missing required fields in input data: %s.';
   SErrMissingInputFields = 'Missing required fields in input data: %s.';
   SErrNoRecordsUpdated = 'No records were updated.';
   SErrNoRecordsUpdated = 'No records were updated.';
   SErrTooManyRecordsUpdated = 'Too many records (%d) were updated.';
   SErrTooManyRecordsUpdated = 'Too many records (%d) were updated.';
+  SErrUnknownParam = 'Unknown parameter "%s"';
+  SErrDuplicateParam = 'Duplicate parameter "%s"';
 
 
 Const
 Const
   DefaultAuthenticationRealm = 'REST API Server';
   DefaultAuthenticationRealm = 'REST API Server';

+ 81 - 11
packages/fcl-web/src/restbridge/sqldbrestdata.pp

@@ -62,9 +62,12 @@ Type
     function GetRequestFields: TSQLDBRestFieldArray;
     function GetRequestFields: TSQLDBRestFieldArray;
     procedure CreateResourceFromDataset(D: TDataset); virtual;
     procedure CreateResourceFromDataset(D: TDataset); virtual;
     procedure DoNotFound; virtual;
     procedure DoNotFound; virtual;
+    procedure SetParamFromStringAndType(P: TParam; S: UTF8String; aDataType: TFieldType); virtual;
     procedure SetPostParams(aParams: TParams; Old : TFields = Nil);virtual;
     procedure SetPostParams(aParams: TParams; Old : TFields = Nil);virtual;
     procedure SetPostFields(aFields: TFields);virtual;
     procedure SetPostFields(aFields: TFields);virtual;
     procedure SetFieldFromData(DataField: TField; ResField: TSQLDBRestField; D: TJSONData); virtual;
     procedure SetFieldFromData(DataField: TField; ResField: TSQLDBRestField; D: TJSONData); virtual;
+    procedure FillParams(aOperation: TRestOperation; aParams: TParams;  FilteredFields: TRestFilterPairArray); virtual;
+
     procedure InsertNewRecord; virtual;
     procedure InsertNewRecord; virtual;
     procedure UpdateExistingRecord(OldData: TDataset; IsPatch : Boolean); virtual;
     procedure UpdateExistingRecord(OldData: TDataset; IsPatch : Boolean); virtual;
     Procedure Notification(AComponent: TComponent; Operation: TOperation); override;
     Procedure Notification(AComponent: TComponent; Operation: TOperation); override;
@@ -74,8 +77,6 @@ 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; 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;
@@ -119,7 +120,7 @@ Type
 
 
 implementation
 implementation
 
 
-uses strutils, variants, dateutils, base64, sqldbrestconst;
+uses typinfo, strutils, variants, dateutils, base64, sqldbrestconst;
 
 
 
 
 Const
 Const
@@ -381,6 +382,7 @@ function TSQLDBRestDBHandler.GetDataForParam(P: TParam; F: TSQLDBRestField;
 Var
 Var
   vs : TVariableSource;
   vs : TVariableSource;
   S,N : UTF8String;
   S,N : UTF8String;
+  RP: TSQLDBRestParam;
 
 
 begin
 begin
   Result:=Nil;
   Result:=Nil;
@@ -403,16 +405,85 @@ begin
       Result:=TJSONString.Create(S)
       Result:=TJSONString.Create(S)
     else if (vsContent in Sources) then
     else if (vsContent in Sources) then
       Result:=IO.RESTInput.GetContentField(N)
       Result:=IO.RESTInput.GetContentField(N)
+    else if vsParam in Sources then
+      begin
+      RP:=FResource.Parameters.Find(N);
+      if Assigned(RP) and (RP.DefaultValue<>'') then
+        Result:=TJSONString.Create(RP.DefaultValue)
+      end;
     end;
     end;
 end;
 end;
 
 
+procedure TSQLDBRestDBHandler.SetParamFromStringAndType(P : TParam; S : UTF8String; aDataType: TFieldType);
+
+var
+  F : Double;
+  C : Integer;
+
+begin
+  Case aDataType of
+
+    ftFmtMemo,
+    ftFixedChar,
+    ftFixedWideChar,
+    ftWideMemo,
+    ftMemo,
+    ftString : P.AsString:=S;
+
+    ftSmallint : P.AsSmallInt:=StrToInt(S);
+    ftInteger : P.AsInteger:=StrToInt(S);
+    ftWord : P.AsWord:=StrToInt(S);
+    ftLargeint : P.AsLargeInt:=StrToInt64(S);
+    ftWideString : P.AsUnicodeString:=S;
+    ftBoolean : P.AsBoolean:=StrToBool(S);
+    ftFloat,
+    ftCurrency,
+    ftFMTBcd,
+    ftBCD :
+      begin
+      Val(S,F,C);
+      if C=0 then
+        P.AsFloat:=F
+      else
+        Raise EConvertError.Create('Invalid float value : '+S);
+      end;
+    ftDate : P.AsDateTime:=ScanDateTime(GetString(rpDateFormat),S);
+    ftTime : P.AsDateTime:=ScanDateTime(GetString(rpDateFormat),S);
+    ftTimeStamp,
+    ftDateTime : P.AsDateTime:=ScanDateTime(GetString(rpDateTimeFormat),S);
+    ftVariant : P.Value:=S;
+    ftBytes : P.AsBytes:=TENcoding.UTF8.GetAnsiBytes(S);
+    ftVarBytes : P.AsBytes:=TENcoding.UTF8.GetAnsiBytes(S);
+    ftBlob : P.AsBytes:=TENcoding.UTF8.GetAnsiBytes(S);
+    ftGUID : P.AsString:=S;
+  else
+    Raise EConvertError.CreateFmt('Unsupported data type: %s',[GetEnumName(TypeInfo(TFieldType),Ord(aDataType))]);
+  end;
+end;
+
 procedure TSQLDBRestDBHandler.SetParamFromData(P: TParam; F: TSQLDBRestField;
 procedure TSQLDBRestDBHandler.SetParamFromData(P: TParam; F: TSQLDBRestField;
   D: TJSONData);
   D: TJSONData);
 
 
+  Procedure OtherParamValue(const S : String);
+
+  var
+    RP : TSQLDBRestParam;
+  begin
+    RP:=Self.FResource.Parameters.Find(P.Name);
+    if assigned(RP) then
+      SetParamFromStringAndType(P,S,RP.DataType)
+    else
+      P.asString:=S;
+  end;
+
 Var
 Var
-  S : String;
+  S : UTF8String;
+  N : String;
 
 
 begin
 begin
+  N:=P.Name;
+  if N='ID' then
+    Writeln('Ah');
   if Assigned(D) then
   if Assigned(D) then
     S:=D.AsString;
     S:=D.AsString;
   if not Assigned(D) then
   if not Assigned(D) then
@@ -434,10 +505,10 @@ begin
          P.AsBlob:=DecodeStringBase64(S);
          P.AsBlob:=DecodeStringBase64(S);
 {$ENDIF}
 {$ENDIF}
     else
     else
-      P.AsString:=S;
+      OtherParamValue(S);
     end
     end
   else
   else
-    P.AsString:=S;
+    OtherParamValue(S);
 end;
 end;
 
 
 function TSQLDBRestDBHandler.FindFieldForParam(aOperation: TRestOperation;
 function TSQLDBRestDBHandler.FindFieldForParam(aOperation: TRestOperation;
@@ -450,14 +521,13 @@ Var
 begin
 begin
   Result:=Nil;
   Result:=Nil;
   N:=P.Name;
   N:=P.Name;
-  if (N='ID_') then
+  Result:=FResource.Fields.FindByFieldName(N);
+  if (Result=Nil) and (N='ID') then
     begin
     begin
     A:=FResource.GetFieldArray(flWhereKey);
     A:=FResource.GetFieldArray(flWhereKey);
     if (Length(A)=1) then
     if (Length(A)=1) then
       Result:=A[0];
       Result:=A[0];
-    end
-  else
-    Result:=FResource.Fields.FindByFieldName(N);
+    end;
 end;
 end;
 
 
 procedure TSQLDBRestDBHandler.FillParams(aOperation : TRestOperation; aParams: TParams;FilteredFields : TRestFilterPairArray);
 procedure TSQLDBRestDBHandler.FillParams(aOperation : TRestOperation; aParams: TParams;FilteredFields : TRestFilterPairArray);
@@ -498,7 +568,7 @@ begin
     end;
     end;
   // Fill in remaining params. Determine source
   // Fill in remaining params. Determine source
   case aOperation of
   case aOperation of
-    roGet : Sources:=[vsQuery,vsRoute];
+    roGet : Sources:=[vsQuery,vsRoute,vsParam];
     roPost,
     roPost,
     roPatch,
     roPatch,
     roPut : Sources:=[vsQuery,vsContent,vsRoute];
     roPut : Sources:=[vsQuery,vsContent,vsRoute];

+ 25 - 3
packages/fcl-web/src/restbridge/sqldbrestio.pp

@@ -74,7 +74,9 @@ Type
                          rpCustomViewResourceName,
                          rpCustomViewResourceName,
                          rpCustomViewSQLParam,
                          rpCustomViewSQLParam,
                          rpXMLDocumentRoot,
                          rpXMLDocumentRoot,
-                         rpConnectionResourceName
+                         rpConnectionResourceName,
+                         rpParametersResourceName,
+                         rpParametersRoutePart
                          );
                          );
   TRestStringProperties = Set of TRestStringProperty;
   TRestStringProperties = Set of TRestStringProperty;
 
 
@@ -127,6 +129,8 @@ Type
     Property OffsetParam : UTF8string Index ord(rpOffset) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
     Property OffsetParam : UTF8string Index ord(rpOffset) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
     Property SortParam : UTF8string Index ord(rpOrderBy) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
     Property SortParam : UTF8string Index ord(rpOrderBy) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
     Property MetadataResourceName : UTF8string Index ord(rpMetadataResourceName) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
     Property MetadataResourceName : UTF8string Index ord(rpMetadataResourceName) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
+    Property MetadataParametersName : UTF8string Index ord(rpParametersResourceName) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
+    Property MetadataParametersRoutePart : UTF8string Index ord(rpParametersRoutePart) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
     Property InputFormatParam : UTF8string Index ord(rpInputFormat) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
     Property InputFormatParam : UTF8string Index ord(rpInputFormat) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
     Property OutputFormatParam : UTF8string Index ord(rpOutputFormat) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
     Property OutputFormatParam : UTF8string Index ord(rpOutputFormat) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
     Property CustomViewResourceName : UTF8string Index ord(rpCustomViewResourceName) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
     Property CustomViewResourceName : UTF8string Index ord(rpCustomViewResourceName) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
@@ -407,6 +411,14 @@ Type
     Function FindStreamerByContentType(aType : TRestStreamerType; const aContentType : string) : TStreamerDef;
     Function FindStreamerByContentType(aType : TRestStreamerType; const aContentType : string) : TStreamerDef;
   end;
   end;
 
 
+  { TRestBufDataset }
+
+  TRestBufDataset = class (TBufDataset)
+  protected
+    procedure LoadBlobIntoBuffer(FieldDef: TFieldDef; ABlobBuf: PBufBlobField); override;
+  end;
+
+
 implementation
 implementation
 
 
 uses base64, dateutils, sqldbrestconst;
 uses base64, dateutils, sqldbrestconst;
@@ -451,7 +463,9 @@ Const
     'customview',      { rpCustomViewResourceName }
     'customview',      { rpCustomViewResourceName }
     'sql',             { rpCustomViewSQLParam }
     'sql',             { rpCustomViewSQLParam }
     'datapacket',      { rpXMLDocumentRoot}
     'datapacket',      { rpXMLDocumentRoot}
-    '_connection'      { rpConnectionResourceName }
+    '_connection',     { rpConnectionResourceName }
+    '_parameters',     { rpParametersResourceName }
+    'parameters'       { rpParametersRoutePart }
   );
   );
   DefaultStatuses : Array[TRestStatus] of Word = (
   DefaultStatuses : Array[TRestStatus] of Word = (
     500, { rsError }
     500, { rsError }
@@ -474,6 +488,14 @@ Const
     200  { rsPatchOK }
     200  { rsPatchOK }
   );
   );
 
 
+{ TRestBufDataset }
+
+procedure TRestBufDataset.LoadBlobIntoBuffer(FieldDef: TFieldDef; ABlobBuf: PBufBlobField);
+begin
+  If (FieldDef=Nil) or (aBlobBuf=Nil) then
+    exit;
+end;
+
 { TRestStatusConfig }
 { TRestStatusConfig }
 
 
 function TRestStatusConfig.GetStatus(AIndex: Integer): Word;
 function TRestStatusConfig.GetStatus(AIndex: Integer): Word;
@@ -981,7 +1003,7 @@ begin
   FContentStream:=TStringStream.Create(aRequest.Content);
   FContentStream:=TStringStream.Create(aRequest.Content);
   FRestContext:=CreateRestContext;
   FRestContext:=CreateRestContext;
   FRestContext.FIO:=Self;
   FRestContext.FIO:=Self;
-  FUpdatedData:=TBufDataset.Create(Nil);
+  FUpdatedData:=TRestBufDataset.Create(Nil);
 end;
 end;
 
 
 destructor TRestIO.Destroy;
 destructor TRestIO.Destroy;

+ 188 - 2
packages/fcl-web/src/restbridge/sqldbrestschema.pp

@@ -43,7 +43,7 @@ Type
   TFieldListKind = (flSelect,flInsert,flInsertParams,flUpdate,flWhereKey,flFilter,flOrderby);
   TFieldListKind = (flSelect,flInsert,flInsertParams,flUpdate,flWhereKey,flFilter,flOrderby);
   TFieldListKinds = set of TFieldListKind;
   TFieldListKinds = set of TFieldListKind;
 
 
-  TVariableSource = (vsNone,vsQuery,vsContent,vsRoute,vsHeader,vsData);
+  TVariableSource = (vsNone,vsQuery,vsContent,vsRoute,vsHeader,vsData,vsParam);
   TVariableSources = Set of TVariableSource;
   TVariableSources = Set of TVariableSource;
 
 
 Const
 Const
@@ -141,6 +141,8 @@ Type
   TSQLDBRestFieldClass = Class of TSQLDBRestField;
   TSQLDBRestFieldClass = Class of TSQLDBRestField;
   TSQLDBRestFieldArray = Array of TSQLDBRestField;
   TSQLDBRestFieldArray = Array of TSQLDBRestField;
 
 
+
+
   { TSQLDBRestFieldArrayHelper }
   { TSQLDBRestFieldArrayHelper }
 
 
   TSQLDBRestFieldArrayHelper = type helper for TSQLDBRestFieldArray
   TSQLDBRestFieldArrayHelper = type helper for TSQLDBRestFieldArray
@@ -185,6 +187,51 @@ Type
   end;
   end;
   TSQLDBRestFieldListClass = Class of TSQLDBRestFieldList;
   TSQLDBRestFieldListClass = Class of TSQLDBRestFieldList;
 
 
+  { TSQLDBRestParam }
+
+  TSQLDBRestParam = class(TCollectionItem)
+  private
+    FName: UTF8String;
+    FDefault: UTF8String;
+    FDataType : TFieldType;
+  Protected
+    Function GetDisplayName: string; override;
+  Public
+    Procedure Assign(Source: TPersistent); override;
+  Published
+    Property Name : UTF8String Read FName Write FName;
+    Property DataType : TFieldType Read FDataType Write FDataType;
+    Property DefaultValue : UTF8String Read FDefault Write FDefault;
+  end;
+  TSQLDBRestParamClass = Class of TSQLDBRestParam;
+  TSQLDBRestParamArray = Array of TSQLDBRestParam;
+
+  { TSQLDBRestParamListEnumerator }
+
+  TSQLDBRestParamListEnumerator = Class(TCollectionEnumerator)
+  Public
+    function GetCurrent: TSQLDBRestParam; reintroduce;
+    property Current: TSQLDBRestParam read GetCurrent;
+  end;
+
+
+  { TSQLDBRestParameterList }
+
+  TSQLDBRestParameterList = class(TCollection)
+  private
+    function GetParam(aIndex : Integer): TSQLDBRestParam;
+    procedure SetParam(aIndex : Integer; AValue: TSQLDBRestParam);
+  Public
+    Function GetEnumerator: TSQLDBRestParamListEnumerator;
+    Function IndexOf(Const aName : string) : Integer;
+    Function Find(Const aName : string) : TSQLDBRestParam;
+    Function ParamByName(Const aName : string) : TSQLDBRestParam;
+    Function AddParam(Const aName : string) : TSQLDBRestParam;
+    Property Params[aIndex : Integer] : TSQLDBRestParam Read GetParam Write SetParam;default;
+  end;
+  TSQLDBRestParameterListClass = Class of TSQLDBRestParameterList;
+
+
   { TSQLDBRestResource }
   { TSQLDBRestResource }
   TSQLDBRestGetDatasetEvent = Procedure (aSender : TObject; aContext : TBaseRestContext; aFieldList : TRestFieldPairArray; aOrderBy : TRestFieldOrderPairArray; aLimit, aOffset : Int64; Var aDataset : TDataset) of object;
   TSQLDBRestGetDatasetEvent = Procedure (aSender : TObject; aContext : TBaseRestContext; aFieldList : TRestFieldPairArray; aOrderBy : TRestFieldOrderPairArray; aLimit, aOffset : Int64; Var aDataset : TDataset) of object;
   TSQLDBRestCheckParamsEvent = Procedure (aSender : TObject; aContext : TBaseRestContext; aOperation : TRestOperation; Params : TParams) of object;
   TSQLDBRestCheckParamsEvent = Procedure (aSender : TObject; aContext : TBaseRestContext; aOperation : TRestOperation; Params : TParams) of object;
@@ -206,6 +253,7 @@ Type
     FOnCheckParams: TSQLDBRestCheckParamsEvent;
     FOnCheckParams: TSQLDBRestCheckParamsEvent;
     FOnGetDataset: TSQLDBRestGetDatasetEvent;
     FOnGetDataset: TSQLDBRestGetDatasetEvent;
     FOnResourceAllowed: TSQLDBRestAllowResourceEvent;
     FOnResourceAllowed: TSQLDBRestAllowResourceEvent;
+    FParameters: TSQLDBRestParameterList;
     FResourceName: UTF8String;
     FResourceName: UTF8String;
     FTableName: UTF8String;
     FTableName: UTF8String;
     FSQL : Array[TSQLKind] of TStrings;
     FSQL : Array[TSQLKind] of TStrings;
@@ -214,6 +262,7 @@ Type
     function GetSQLTyped(aKind : TSQLKind): TStrings;
     function GetSQLTyped(aKind : TSQLKind): TStrings;
     procedure SetAllowedOperations(AValue: TRestOperations);
     procedure SetAllowedOperations(AValue: TRestOperations);
     procedure SetFields(AValue: TSQLDBRestFieldList);
     procedure SetFields(AValue: TSQLDBRestFieldList);
+    procedure SetParameters(AValue: TSQLDBRestParameterList);
     procedure SetSQL(AIndex: Integer; AValue: TStrings);
     procedure SetSQL(AIndex: Integer; AValue: TStrings);
   Protected
   Protected
     Function GetDisplayName: string; override;
     Function GetDisplayName: string; override;
@@ -221,8 +270,12 @@ Type
     Class var
     Class var
       DefaultFieldListClass : TSQLDBRestFieldListClass;
       DefaultFieldListClass : TSQLDBRestFieldListClass;
       DefaultFieldClass: TSQLDBRestFieldClass;
       DefaultFieldClass: TSQLDBRestFieldClass;
+      DefaultParameterListClass : TSQLDBRestParameterListClass;
+      DefaultParamClass : TSQLDBRestParamClass;
     Class function CreateFieldList : TSQLDBRestFieldList; virtual;
     Class function CreateFieldList : TSQLDBRestFieldList; virtual;
+    Class function CreateParamList : TSQLDBRestParameterList; virtual;
     Class function FieldTypeToRestFieldType(aFieldType: TFieldType): TRestFieldType; virtual;
     Class function FieldTypeToRestFieldType(aFieldType: TFieldType): TRestFieldType; virtual;
+    Class Constructor Init;
   Public
   Public
     Constructor Create(ACollection: TCollection); override;
     Constructor Create(ACollection: TCollection); override;
     Destructor Destroy; override;
     Destructor Destroy; override;
@@ -240,10 +293,12 @@ Type
     Function GetResolvedSQl(aKind : TSQLKind; Const AWhere : UTF8String; Const aOrderBy : UTF8String = ''; aLimit : UTF8String = ''; OnlyFields : TSQLDBRestFieldArray = nil) : UTF8String;
     Function GetResolvedSQl(aKind : TSQLKind; Const AWhere : UTF8String; Const aOrderBy : UTF8String = ''; aLimit : UTF8String = ''; OnlyFields : TSQLDBRestFieldArray = nil) : UTF8String;
     Function ProcessSQl(aSQL : String; Const AWhere : UTF8String; Const aOrderBy : UTF8String = ''; aLimit : UTF8String = '') : UTF8String;
     Function ProcessSQl(aSQL : String; Const AWhere : UTF8String; Const aOrderBy : UTF8String = ''; aLimit : UTF8String = '') : UTF8String;
     Procedure PopulateFieldsFromFieldDefs(Defs : TFieldDefs; aIndexFields : TStringArray; aProcessIdentifier : TProcessIdentifier; aMinFieldOpts : TRestFieldOptions);
     Procedure PopulateFieldsFromFieldDefs(Defs : TFieldDefs; aIndexFields : TStringArray; aProcessIdentifier : TProcessIdentifier; aMinFieldOpts : TRestFieldOptions);
+    Procedure PopulateParametersFromSQL(const SQL : String; DoClear : Boolean = True);
     Property SQL [aKind : TSQLKind] : TStrings Read GetSQLTyped;
     Property SQL [aKind : TSQLKind] : TStrings Read GetSQLTyped;
     Property BusinessProcessor : TSQLDBRestCustomBusinessProcessor Read FBusinessProcessor;
     Property BusinessProcessor : TSQLDBRestCustomBusinessProcessor Read FBusinessProcessor;
   Published
   Published
     Property Fields : TSQLDBRestFieldList Read FFields Write SetFields;
     Property Fields : TSQLDBRestFieldList Read FFields Write SetFields;
+    Property Parameters : TSQLDBRestParameterList Read FParameters Write SetParameters;
     Property Enabled : Boolean Read FEnabled Write FEnabled default true;
     Property Enabled : Boolean Read FEnabled Write FEnabled default true;
     Property InMetadata : Boolean Read FInMetadata Write FInMetadata default true;
     Property InMetadata : Boolean Read FInMetadata Write FInMetadata default true;
     Property ConnectionName : UTF8String read FConnectionName Write FConnectionName;
     Property ConnectionName : UTF8String read FConnectionName Write FConnectionName;
@@ -387,6 +442,90 @@ implementation
 
 
 uses strutils, fpjsonrtti,dbconst, sqldbrestconst;
 uses strutils, fpjsonrtti,dbconst, sqldbrestconst;
 
 
+{ TSQLDBRestParam }
+
+function TSQLDBRestParam.GetDisplayName: string;
+begin
+  Result:=Name;
+  if Result='' then
+    Result:=inherited GetDisplayName;
+end;
+
+procedure TSQLDBRestParam.Assign(Source: TPersistent);
+
+var
+  P : TSQLDBRestParam absolute Source;
+
+begin
+  if Source is TSQLDBRestParam then
+    begin
+    FName:=P.Name;
+    FDataType:=P.DataType;
+    FDefault:=P.DefaultValue;
+    end
+  else
+    inherited Assign(Source);
+end;
+
+{ TSQLDBRestParamListEnumerator }
+
+function TSQLDBRestParamListEnumerator.GetCurrent: TSQLDBRestParam;
+begin
+  Result:=TSQLDBRestParam(Inherited GetCurrent);
+end;
+
+{ TSQLDBRestParameterList }
+
+function TSQLDBRestParameterList.GetParam(aIndex : Integer): TSQLDBRestParam;
+begin
+  Result:=Items[aIndex] as TSQLDBRestParam;
+end;
+
+procedure TSQLDBRestParameterList.SetParam(aIndex : Integer; AValue: TSQLDBRestParam);
+begin
+  Items[aIndex]:=aValue;
+end;
+
+function TSQLDBRestParameterList.GetEnumerator: TSQLDBRestParamListEnumerator;
+begin
+  Result:=TSQLDBRestParamListEnumerator.Create(Self);
+end;
+
+function TSQLDBRestParameterList.IndexOf(const aName: string): Integer;
+begin
+  Result:=Count-1;
+  While (Result>=0) and Not SameText(aName,GetParam(Result).Name) do
+    Dec(Result);
+end;
+
+function TSQLDBRestParameterList.Find(const aName: string): TSQLDBRestParam;
+
+var
+  Idx : Integer;
+
+begin
+  Result:=Nil;
+  Idx:=IndexOf(aName);
+  if (Idx<>-1) then
+    Result:=GetParam(Idx);
+end;
+
+function TSQLDBRestParameterList.ParamByName(const aName: string): TSQLDBRestParam;
+begin
+  Result:=Find(aName);
+  if Result=Nil then
+    Raise ESQLDBRest.CreateFmt(500,SErrUnknownParam,[aName]);
+end;
+
+function TSQLDBRestParameterList.AddParam(const aName: string): TSQLDBRestParam;
+begin
+  if IndexOf(aName)<>-1 then
+    Raise ESQLDBRest.CreateFmt(500,SErrDuplicateParam,[aName]);
+  Result:=Add as TSQLDBRestParam;
+  Result.Name:=aName;
+  Result.DataType:=ftString;
+end;
+
 { TSQLDBRestFieldListEnumerator }
 { TSQLDBRestFieldListEnumerator }
 
 
 function TSQLDBRestFieldListEnumerator.GetCurrent: TSQLDBRestField;
 function TSQLDBRestFieldListEnumerator.GetCurrent: TSQLDBRestField;
@@ -1026,7 +1165,13 @@ end;
 procedure TSQLDBRestResource.SetFields(AValue: TSQLDBRestFieldList);
 procedure TSQLDBRestResource.SetFields(AValue: TSQLDBRestFieldList);
 begin
 begin
   if FFields=AValue then Exit;
   if FFields=AValue then Exit;
-  FFields:=AValue;
+  FFields.Assign(AValue);
+end;
+
+procedure TSQLDBRestResource.SetParameters(AValue: TSQLDBRestParameterList);
+begin
+  if FParameters=AValue then Exit;
+  FParameters.Assign(AValue);
 end;
 end;
 
 
 procedure TSQLDBRestResource.SetSQL(AIndex: Integer; AValue: TStrings);
 procedure TSQLDBRestResource.SetSQL(AIndex: Integer; AValue: TStrings);
@@ -1047,6 +1192,7 @@ Var
 begin
 begin
   inherited Create(ACollection);
   inherited Create(ACollection);
   FFields:=CreateFieldList;
   FFields:=CreateFieldList;
+  FParameters:=CreateParamList;
   FEnabled:=True;
   FEnabled:=True;
   FInMetadata:=True;
   FInMetadata:=True;
   for K in TSQLKind do
   for K in TSQLKind do
@@ -1106,6 +1252,7 @@ begin
     for K in TSQLKind do
     for K in TSQLKind do
       SQL[K].Assign(R.SQL[K]);
       SQL[K].Assign(R.SQL[K]);
     Fields.Assign(R.Fields);
     Fields.Assign(R.Fields);
+    Parameters.Assign(R.Parameters);
     TableName:=R.TableName;
     TableName:=R.TableName;
     FResourceName:=R.FResourceName;
     FResourceName:=R.FResourceName;
     ConnectionName:=R.ConnectionName;
     ConnectionName:=R.ConnectionName;
@@ -1351,6 +1498,15 @@ begin
   Result:=Map[aFieldType];
   Result:=Map[aFieldType];
 end;
 end;
 
 
+class constructor TSQLDBRestResource.Init;
+begin
+  DefaultFieldListClass:=TSQLDBRestFieldList;
+  DefaultFieldClass:=TSQLDBRestField;
+  DefaultParameterListClass:=TSQLDBRestParameterList;
+  DefaultParamClass:=TSQLDBRestParam;
+
+end;
+
 procedure TSQLDBRestResource.PopulateFieldsFromFieldDefs(Defs: TFieldDefs; aIndexFields: TStringArray;
 procedure TSQLDBRestResource.PopulateFieldsFromFieldDefs(Defs: TFieldDefs; aIndexFields: TStringArray;
   aProcessIdentifier: TProcessIdentifier; aMinFieldOpts: TRestFieldOptions);
   aProcessIdentifier: TProcessIdentifier; aMinFieldOpts: TRestFieldOptions);
 
 
@@ -1390,12 +1546,42 @@ begin
     end;
     end;
 end;
 end;
 
 
+procedure TSQLDBRestResource.PopulateParametersFromSQL(const SQL: String;
+  DoClear: Boolean);
+
+Var
+  Parms : TParams;
+  P : TParam;
+  SRP : TSQLDBRestParam;
+
+begin
+  if DoClear then
+    Parameters.Clear;
+  Parms:=TParams.Create(Nil);
+  try
+    Parms.ParseSQL(SQL,True);
+    for P in Parms do
+      If Parameters.IndexOf(P.Name)=-1 then
+        begin
+        SRP:=Parameters.AddParam(P.Name);
+        SRP.DataType:=ftString;
+        end;
+  finally
+    Parms.Free;
+  end;
+end;
+
 class function TSQLDBRestResource.CreateFieldList: TSQLDBRestFieldList;
 class function TSQLDBRestResource.CreateFieldList: TSQLDBRestFieldList;
 
 
 begin
 begin
   Result:=DefaultFieldListClass.Create(DefaultFieldClass);
   Result:=DefaultFieldListClass.Create(DefaultFieldClass);
 end;
 end;
 
 
+class function TSQLDBRestResource.CreateParamList: TSQLDBRestParameterList;
+begin
+  Result:=DefaultParameterListClass.Create(DefaultParamClass);
+end;
+
 { TSQLDBRestFieldList }
 { TSQLDBRestFieldList }
 
 
 function TSQLDBRestFieldList.GetFields(aIndex: Integer): TSQLDBRestField;
 function TSQLDBRestFieldList.GetFields(aIndex: Integer): TSQLDBRestField;