Repository URL to install this package:
|
Version:
3.2.0 ▾
|
{
This file is part of the Free Pascal run time library.
Copyright (c) 2019 by the Free Pascal development team
SQLDB REST CDS input/output
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
unit sqldbrestcds;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, DateUtils, db,fpjson, dom, XMLRead, XMLWrite,sqldbrestschema,sqldbrestio, sqldbrestbridge;
Type
{ TCDSInputStreamer }
TCDSInputStreamer = Class(TRestInputStreamer)
private
FXML: TXMLDocument;
FPacket : TDOMElement;
FROWData : TDOMElement;
FRow : TDOMElement;
Public
Destructor Destroy; override;
Class Function GetContentType: String; override;
Class Function ForBufDataset: Boolean; virtual;
Function SelectObject(aIndex : Integer) : Boolean; override;
function GetContentField(aName: UTF8string): TJSONData; override;
procedure InitStreaming; override;
Property XML : TXMLDocument Read FXML;
Property Packet : TDOMElement Read FPacket;
Property RowData : TDOMElement Read FRowData;
Property Row : TDOMElement Read FRow;
end;
{ TCDSOutputStreamer }
TCDSOutputStreamer = Class(TRestOutputStreamer)
Private
FXML: TXMLDocument;
FDataPacket : TDOMElement;
FMetaData : TDOMElement;
FRow : TDOMElement;
FRowData: TDOMElement;
Protected
Class Function ForBufDataset: Boolean; virtual;
Procedure SetOutputOptions(AValue: TRestOutputOptions); override;
Public
procedure EndData; override;
procedure EndRow; override;
procedure FinalizeOutput; override;
procedure StartData; override;
procedure StartRow; override;
// Return Nil for null field.
procedure WriteField(aPair: TRestFieldPair); override;
procedure WriteMetadata(aFieldList: TRestFieldPairArray); override;
Procedure CreateErrorContent(aCode : Integer; Const aMessage: String); override;
Property XML : TXMLDocument Read FXML;
Property RowData : TDOMelement Read FRowData;
Property Row : TDOMelement Read FRow;
Property Metadata : TDOMelement Read FMetadata;
Public
Destructor Destroy; override;
Class Function GetContentType: String; override;
procedure InitStreaming; override;
end;
{ TBufDatasetOutputStreamer }
TBufDatasetOutputStreamer = Class(TCDSOutputStreamer)
Protected
Class Function ForBufDataset: Boolean; override;
end;
{ TBufDatasetInputStreamer }
TBufDatasetInputStreamer = Class(TCDSInputStreamer)
Protected
Class Function ForBufDataset: Boolean; override;
end;
implementation
uses sqldbrestconst;
Const
DateTimeFmt = 'yyyymmddThh:nn:sszzz';
Const
XMLPropTypeNames : Array [TRestFieldType] of UnicodeString = (
'Unknown' {rftUnknown},
'i4' {rftInteger},
'i8' {rftLargeInt},
'r8' {rftFloat},
'dateTime' {rftDate},
'dateTime' {rftTime},
'dateTime' {rftDateTime},
'string' {rftString},
'boolean' {rftBoolean},
'bin.hex:Binary' {rftBlob}
);
{ TBufDatasetInputStreamer }
class function TBufDatasetInputStreamer.ForBufDataset: Boolean;
begin
Result:=True;
end;
{ TBufDatasetOutputStreamer }
class function TBufDatasetOutputStreamer.ForBufDataset: Boolean;
begin
Result:=True;
end;
{ TCDSInputStreamer }
destructor TCDSInputStreamer.Destroy;
begin
FreeAndNil(FXML);
inherited Destroy;
end;
class function TCDSInputStreamer.GetContentType: String;
begin
Result:='text/xml';
end;
class function TCDSInputStreamer.ForBufDataset: Boolean;
begin
Result:=False;
end;
function TCDSInputStreamer.SelectObject(aIndex: Integer): Boolean;
Var
N : TDomNode;
NN : UnicodeString;
begin
Result:=False;
NN:='ROW';
N:=FRowData.FindNode(NN);
if Not (Assigned(N) and (N is TDOMelement)) then
raise ESQLDBRest.CreateFmt(400, SErrInvalidCDSMissingElement,[NN]);
While (aIndex>0) and (N<>Nil) and (N.NodeName<>NN) and (N.NodeType<>ELEMENT_NODE) do
begin
N:=N.NextSibling;
Dec(aIndex);
end;
Result:=(aIndex=0) and (N<>Nil);
If Result then
FRow:=N as TDomElement
else
FRow:=Nil;
end;
function TCDSInputStreamer.GetContentField(aName: UTF8string): TJSONData;
Var
NN : UnicodeString;
begin
NN:=UTF8Decode(aName);
if Assigned(FRow) and FRow.hasAttribute(NN) then
Result:=TJSONString.Create(FRow.AttribStrings[NN])
else
Result:=Nil;
end;
procedure TCDSInputStreamer.InitStreaming;
Var
Msg : String;
N : TDomNode;
begin
FreeAndNil(FXML);
if Stream.Size<=0 then
exit;
try
ReadXMLFile(FXML,Stream);
except
On E : Exception do
begin
Msg:=E.Message;
FXML:=Nil;
end;
end;
if (FXML=Nil) then
raise ESQLDBRest.CreateFmt(400, SErrInvalidXMLInput, [Msg]);
FPacket:=FXML.DocumentElement;
if (FPacket=Nil) then
raise ESQLDBRest.CreateFmt(400, SErrInvalidXMLInput, [SErrMissingDocumentRoot]);
if (FPacket.NodeName<>'DATAPACKET') then
Raise ESQLDBRest.CreateFmt(400,SErrInvalidCDSMissingElement,['DATAPACKET']);
N:=FPacket.FindNode('ROWDATA');
if Not (Assigned(N) and (N is TDOMelement)) then
Raise ESQLDBRest.CreateFmt(400,SErrInvalidCDSMissingElement,[ROWDATA]);
FRowData:=(N as TDOMelement);
end;
{ TCDSOutputStreamer }
class function TCDSOutputStreamer.ForBufDataset: Boolean;
begin
Result:=False;
end;
procedure TCDSOutputStreamer.SetOutputOptions(AValue: TRestOutputOptions);
begin
Include(AValue,ooMetadata); // We always need metadata
inherited SetOutputOptions(AValue);
end;
procedure TCDSOutputStreamer.EndData;
begin
FRowData:=Nil;
end;
procedure TCDSOutputStreamer.EndRow;
begin
FRow:=Nil;
end;
procedure TCDSOutputStreamer.FinalizeOutput;
begin
{$IFNDEF VER3_0}
if Not (ooHumanReadable in OutputOptions) then
begin
With TDOMWriter.Create(Stream,FXML) do
try
LineBreak:='';
IndentSize:=0;
WriteNode(FXML);
finally
Free;
end;
end
else
{$ENDIF}
xmlwrite.WriteXML(FXML,Stream);
FreeAndNil(FXML);
end;
procedure TCDSOutputStreamer.StartData;
begin
// Do nothing
end;
procedure TCDSOutputStreamer.StartRow;
begin
if (FRow<>Nil) then
Raise ESQLDBRest.Create(500,SErrDoubleRowStart);
FRow:=FXML.CreateElement('ROW');
FRowData.AppendChild(FRow);
end;
procedure TCDSOutputStreamer.WriteField(aPair: TRestFieldPair);
Var
N : UTF8String;
S : UTF8String;
F : TField;
begin
N:=aPair.RestField.PublicName;
if FRow=Nil then
Raise ESQLDBRest.CreateFmt(500,SErrFieldWithoutRow,[N]);
F:=aPair.DBField;
If (aPair.RestField.FieldType=rftUnknown) then
raise ESQLDBRest.CreateFmt(500,SErrUnsupportedRestFieldType, [N]);
If (F.IsNull) then
Exit;
if (aPair.RestField.FieldType in [rftDate,rftTime,rftDateTime]) then
S:=FormatDateTime(DateTimeFmt,F.AsDateTime)
else
S:=FieldToString(aPair.RestField.FieldType,F);
FRow[UTF8Decode(N)]:=UTF8Decode(S);
end;
procedure TCDSOutputStreamer.WriteMetadata(aFieldList: TRestFieldPairArray);
Var
FL,F : TDOMElement;
P : TREstFieldPair;
S,ST : UnicodeString;
ml : Integer;
begin
FL:=FXML.CreateElement('FIELDS');
FMetaData.AppendChild(FL);
For P in aFieldList do
begin
S:=XMLPropTypeNames[P.RestField.FieldType];
if (S<>'') then
begin
ST:='';
if P.RestField.PublicName='ID' then
ST:='autoinc';
F:=FXML.CreateElement('FIELD');
F['attrname']:=Utf8Decode(P.RestField.PublicName);
F['fieldtype']:=S;
if P.RestField.FieldType=rftString then
begin
ML:=P.RestField.MaxLen;
if ML=0 then
ML:=255;
if ForBufDataset then
F['width']:=Utf8Decode(IntToStr(P.RestField.MaxLen))
else
F['WIDTH']:=Utf8Decode(IntToStr(P.RestField.MaxLen));
end;
if (ST<>'') then
F['subtype']:=ST;
FL.AppendChild(F);
end;
end;
end;
class function TCDSOutputStreamer.GetContentType: String;
begin
Result:='text/xml';
end;
procedure TCDSOutputStreamer.CreateErrorContent(aCode: Integer; const aMessage: String);
Var
ErrorObj : TDomElement;
begin
ErrorObj:=FXML.CreateElement(UTF8Decode(GetString(rpErrorRoot)));
ErrorObj['code']:=UTF8Decode(IntToStr(aCode));
ErrorObj['message']:=UTF8Decode(aMessage);
FDataPacket.AppendChild(ErrorObj);
end;
destructor TCDSOutputStreamer.Destroy;
begin
FreeAndNil(FXML);
inherited Destroy;
end;
procedure TCDSOutputStreamer.InitStreaming;
begin
FXML:=TXMLDocument.Create;
FDataPacket:=FXML.CreateElement('DATAPACKET');
FXML.AppendChild(FDataPacket);
FDataPacket['Version']:='2.0';
FMetaData:=FXML.CreateElement('METADATA');
FDataPacket.AppendChild(FMetaData);
FRowData:=FXML.CreateElement('ROWDATA');
FDataPacket.AppendChild(FRowData);
end;
Initialization
TCDSInputStreamer.RegisterStreamer('cds');
TBufDatasetInputStreamer.RegisterStreamer('buf');
TCDSOutputStreamer.RegisterStreamer('cds');
TBufDatasetOutputStreamer.RegisterStreamer('buf');
end.