sqldbrestcds.pp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2019 by the Free Pascal development team
  4. SQLDB REST CDS input/output
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$IFNDEF FPC_DOTTEDUNITS}
  12. unit sqldbrestcds;
  13. {$ENDIF FPC_DOTTEDUNITS}
  14. {$mode objfpc}{$H+}
  15. interface
  16. {$IFDEF FPC_DOTTEDUNITS}
  17. uses
  18. System.Classes, System.SysUtils, System.DateUtils, Data.Db, FpJson.Data, Xml.Dom, Xml.Read,
  19. Xml.Writer,FpWeb.RestBridge.Schema,FpWeb.RestBridge.IO, FpWeb.RestBridge.Bridge;
  20. {$ELSE FPC_DOTTEDUNITS}
  21. uses
  22. Classes, SysUtils, DateUtils, db,fpjson, dom, XMLRead, XMLWrite,sqldbrestschema,sqldbrestio, sqldbrestbridge;
  23. {$ENDIF FPC_DOTTEDUNITS}
  24. Type
  25. { TCDSInputStreamer }
  26. TCDSInputStreamer = Class(TRestInputStreamer)
  27. private
  28. FXML: TXMLDocument;
  29. FPacket : TDOMElement;
  30. FROWData : TDOMElement;
  31. FRow : TDOMElement;
  32. Public
  33. Destructor Destroy; override;
  34. Class Function GetContentType: String; override;
  35. Class Function ForBufDataset: Boolean; virtual;
  36. Function SelectObject(aIndex : Integer) : Boolean; override;
  37. function GetContentField(aName: UTF8string): TJSONData; override;
  38. procedure InitStreaming; override;
  39. Property XML : TXMLDocument Read FXML;
  40. Property Packet : TDOMElement Read FPacket;
  41. Property RowData : TDOMElement Read FRowData;
  42. Property Row : TDOMElement Read FRow;
  43. end;
  44. { TCDSOutputStreamer }
  45. TCDSOutputStreamer = Class(TRestOutputStreamer)
  46. Private
  47. FXML: TXMLDocument;
  48. FDataPacket : TDOMElement;
  49. FMetaData : TDOMElement;
  50. FRow : TDOMElement;
  51. FRowData: TDOMElement;
  52. Protected
  53. Class Function ForBufDataset: Boolean; virtual;
  54. Procedure SetOutputOptions(AValue: TRestOutputOptions); override;
  55. Public
  56. procedure EndData; override;
  57. procedure EndRow; override;
  58. procedure FinalizeOutput; override;
  59. procedure StartData; override;
  60. procedure StartRow; override;
  61. // Return Nil for null field.
  62. procedure WriteField(aPair: TRestFieldPair); override;
  63. procedure WriteMetadata(aFieldList: TRestFieldPairArray); override;
  64. Procedure CreateErrorContent(aCode : Integer; Const aMessage: String); override;
  65. Property XML : TXMLDocument Read FXML;
  66. Property RowData : TDOMelement Read FRowData;
  67. Property Row : TDOMelement Read FRow;
  68. Property Metadata : TDOMelement Read FMetadata;
  69. Public
  70. Destructor Destroy; override;
  71. Class Function GetContentType: String; override;
  72. Class Function FileExtension : String; override;
  73. procedure InitStreaming; override;
  74. end;
  75. { TBufDatasetOutputStreamer }
  76. TBufDatasetOutputStreamer = Class(TCDSOutputStreamer)
  77. Protected
  78. Class Function ForBufDataset: Boolean; override;
  79. end;
  80. { TBufDatasetInputStreamer }
  81. TBufDatasetInputStreamer = Class(TCDSInputStreamer)
  82. Public
  83. Class Function ForBufDataset: Boolean; override;
  84. end;
  85. implementation
  86. {$IFDEF FPC_DOTTEDUNITS}
  87. uses FpWeb.RestBridge.Consts;
  88. {$ELSE FPC_DOTTEDUNITS}
  89. uses sqldbrestconst;
  90. {$ENDIF FPC_DOTTEDUNITS}
  91. Const
  92. DateTimeFmt = 'yyyymmddThh:nn:sszzz';
  93. Const
  94. XMLPropTypeNames : Array [TRestFieldType] of UnicodeString = (
  95. 'Unknown' {rftUnknown},
  96. 'i4' {rftInteger},
  97. 'i8' {rftLargeInt},
  98. 'r8' {rftFloat},
  99. 'dateTime' {rftDate},
  100. 'dateTime' {rftTime},
  101. 'dateTime' {rftDateTime},
  102. 'string' {rftString},
  103. 'boolean' {rftBoolean},
  104. 'bin.hex:Binary' {rftBlob}
  105. );
  106. { TBufDatasetInputStreamer }
  107. class function TBufDatasetInputStreamer.ForBufDataset: Boolean;
  108. begin
  109. Result:=True;
  110. end;
  111. { TBufDatasetOutputStreamer }
  112. class function TBufDatasetOutputStreamer.ForBufDataset: Boolean;
  113. begin
  114. Result:=True;
  115. end;
  116. { TCDSInputStreamer }
  117. destructor TCDSInputStreamer.Destroy;
  118. begin
  119. FreeAndNil(FXML);
  120. inherited Destroy;
  121. end;
  122. class function TCDSInputStreamer.GetContentType: String;
  123. begin
  124. Result:='text/xml';
  125. end;
  126. class function TCDSInputStreamer.ForBufDataset: Boolean;
  127. begin
  128. Result:=False;
  129. end;
  130. function TCDSInputStreamer.SelectObject(aIndex: Integer): Boolean;
  131. Var
  132. N : TDomNode;
  133. NN : UnicodeString;
  134. begin
  135. Result:=False;
  136. NN:='ROW';
  137. N:=FRowData.FindNode(NN);
  138. if Not (Assigned(N) and (N is TDOMelement)) then
  139. raise ESQLDBRest.CreateFmt(400, SErrInvalidCDSMissingElement,[NN]);
  140. While (aIndex>0) and (N<>Nil) and (N.NodeName<>NN) and (N.NodeType<>ELEMENT_NODE) do
  141. begin
  142. N:=N.NextSibling;
  143. Dec(aIndex);
  144. end;
  145. Result:=(aIndex=0) and (N<>Nil);
  146. If Result then
  147. FRow:=N as TDomElement
  148. else
  149. FRow:=Nil;
  150. end;
  151. function TCDSInputStreamer.GetContentField(aName: UTF8string): TJSONData;
  152. Var
  153. NN : UnicodeString;
  154. begin
  155. NN:=UTF8Decode(aName);
  156. if Assigned(FRow) and FRow.hasAttribute(NN) then
  157. Result:=TJSONString.Create(FRow.AttribStrings[NN])
  158. else
  159. Result:=Nil;
  160. end;
  161. procedure TCDSInputStreamer.InitStreaming;
  162. Var
  163. Msg : String;
  164. N : TDomNode;
  165. begin
  166. FreeAndNil(FXML);
  167. if Stream.Size<=0 then
  168. exit;
  169. try
  170. ReadXMLFile(FXML,Stream);
  171. except
  172. On E : Exception do
  173. begin
  174. Msg:=E.Message;
  175. FXML:=Nil;
  176. end;
  177. end;
  178. if (FXML=Nil) then
  179. raise ESQLDBRest.CreateFmt(400, SErrInvalidXMLInput, [Msg]);
  180. FPacket:=FXML.DocumentElement;
  181. if (FPacket=Nil) then
  182. raise ESQLDBRest.CreateFmt(400, SErrInvalidXMLInput, [SErrMissingDocumentRoot]);
  183. if (FPacket.NodeName<>'DATAPACKET') then
  184. Raise ESQLDBRest.CreateFmt(400,SErrInvalidCDSMissingElement,['DATAPACKET']);
  185. N:=FPacket.FindNode('ROWDATA');
  186. if Not (Assigned(N) and (N is TDOMelement)) then
  187. Raise ESQLDBRest.CreateFmt(400,SErrInvalidCDSMissingElement,[ROWDATA]);
  188. FRowData:=(N as TDOMelement);
  189. end;
  190. { TCDSOutputStreamer }
  191. class function TCDSOutputStreamer.ForBufDataset: Boolean;
  192. begin
  193. Result:=False;
  194. end;
  195. procedure TCDSOutputStreamer.SetOutputOptions(AValue: TRestOutputOptions);
  196. begin
  197. Include(AValue,ooMetadata); // We always need metadata
  198. inherited SetOutputOptions(AValue);
  199. end;
  200. procedure TCDSOutputStreamer.EndData;
  201. begin
  202. FRowData:=Nil;
  203. end;
  204. procedure TCDSOutputStreamer.EndRow;
  205. begin
  206. FRow:=Nil;
  207. end;
  208. procedure TCDSOutputStreamer.FinalizeOutput;
  209. begin
  210. {$IFNDEF VER3_0}
  211. if Not (ooHumanReadable in OutputOptions) then
  212. begin
  213. With TDOMWriter.Create(Stream,FXML) do
  214. try
  215. LineBreak:='';
  216. IndentSize:=0;
  217. WriteNode(FXML);
  218. finally
  219. Free;
  220. end;
  221. end
  222. else
  223. {$ENDIF}
  224. WriteXML(FXML,Stream);
  225. FreeAndNil(FXML);
  226. end;
  227. procedure TCDSOutputStreamer.StartData;
  228. begin
  229. // Do nothing
  230. end;
  231. procedure TCDSOutputStreamer.StartRow;
  232. begin
  233. if (FRow<>Nil) then
  234. Raise ESQLDBRest.Create(500,SErrDoubleRowStart);
  235. FRow:=FXML.CreateElement('ROW');
  236. FRowData.AppendChild(FRow);
  237. end;
  238. procedure TCDSOutputStreamer.WriteField(aPair: TRestFieldPair);
  239. Var
  240. N : UTF8String;
  241. S : UTF8String;
  242. F : TField;
  243. begin
  244. N:=aPair.RestField.PublicName;
  245. if FRow=Nil then
  246. Raise ESQLDBRest.CreateFmt(500,SErrFieldWithoutRow,[N]);
  247. F:=aPair.DBField;
  248. If (aPair.RestField.FieldType=rftUnknown) then
  249. raise ESQLDBRest.CreateFmt(500,SErrUnsupportedRestFieldType, [N]);
  250. If (F.IsNull) then
  251. Exit;
  252. if (aPair.RestField.FieldType in [rftDate,rftTime,rftDateTime]) then
  253. S:=FormatDateTime(DateTimeFmt,F.AsDateTime)
  254. else
  255. S:=FieldToString(aPair.RestField.FieldType,F);
  256. FRow[UTF8Decode(N)]:=UTF8Decode(S);
  257. end;
  258. procedure TCDSOutputStreamer.WriteMetadata(aFieldList: TRestFieldPairArray);
  259. Var
  260. FL,F : TDOMElement;
  261. P : TREstFieldPair;
  262. S,ST : UnicodeString;
  263. ml : Integer;
  264. begin
  265. FL:=FXML.CreateElement('FIELDS');
  266. FMetaData.AppendChild(FL);
  267. For P in aFieldList do
  268. begin
  269. S:=XMLPropTypeNames[P.RestField.FieldType];
  270. if (S<>'') then
  271. begin
  272. ST:='';
  273. if P.RestField.PublicName='ID' then
  274. ST:='autoinc';
  275. F:=FXML.CreateElement('FIELD');
  276. F['attrname']:=Utf8Decode(P.RestField.PublicName);
  277. F['fieldtype']:=S;
  278. if P.RestField.FieldType=rftString then
  279. begin
  280. ML:=P.RestField.MaxLen;
  281. if ML=0 then
  282. ML:=255;
  283. if ForBufDataset then
  284. F['width']:=Utf8Decode(IntToStr(P.RestField.MaxLen))
  285. else
  286. F['WIDTH']:=Utf8Decode(IntToStr(P.RestField.MaxLen));
  287. end;
  288. if (ST<>'') then
  289. F['subtype']:=ST;
  290. FL.AppendChild(F);
  291. end;
  292. end;
  293. end;
  294. class function TCDSOutputStreamer.GetContentType: String;
  295. begin
  296. Result:='text/xml';
  297. end;
  298. class function TCDSOutputStreamer.FileExtension: String;
  299. begin
  300. Result:='xml';
  301. end;
  302. procedure TCDSOutputStreamer.CreateErrorContent(aCode: Integer; const aMessage: String);
  303. Var
  304. ErrorObj : TDomElement;
  305. begin
  306. ErrorObj:=FXML.CreateElement(UTF8Decode(GetString(rpErrorRoot)));
  307. ErrorObj['code']:=UTF8Decode(IntToStr(aCode));
  308. ErrorObj['message']:=UTF8Decode(aMessage);
  309. FDataPacket.AppendChild(ErrorObj);
  310. end;
  311. destructor TCDSOutputStreamer.Destroy;
  312. begin
  313. FreeAndNil(FXML);
  314. inherited Destroy;
  315. end;
  316. procedure TCDSOutputStreamer.InitStreaming;
  317. begin
  318. FXML:=TXMLDocument.Create;
  319. FDataPacket:=FXML.CreateElement('DATAPACKET');
  320. FXML.AppendChild(FDataPacket);
  321. FDataPacket['Version']:='2.0';
  322. FMetaData:=FXML.CreateElement('METADATA');
  323. FDataPacket.AppendChild(FMetaData);
  324. FRowData:=FXML.CreateElement('ROWDATA');
  325. FDataPacket.AppendChild(FRowData);
  326. end;
  327. Initialization
  328. TCDSInputStreamer.RegisterStreamer('cds');
  329. TBufDatasetInputStreamer.RegisterStreamer('buf');
  330. TCDSOutputStreamer.RegisterStreamer('cds');
  331. TBufDatasetOutputStreamer.RegisterStreamer('buf');
  332. end.