dsqlbuilder.pas 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. (*
  2. Duall Sistemas, SQL Builder Classes
  3. Copyright (C) 2014 Silvio Clecio
  4. See the file LICENSE.txt, included in this distribution,
  5. for details about the copyright.
  6. This library is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. *)
  10. unit dSqlBuilder;
  11. {$i dopf.inc}
  12. interface
  13. uses
  14. dClasses, dUtils, Classes, SysUtils, TypInfo;
  15. type
  16. EdTable = class(EdException);
  17. EdSqlBuilder = class(EdException);
  18. { TdGTable }
  19. generic TdGTable<T> = class(TdObject)
  20. private
  21. FPrimaryKeys: TStrings;
  22. FPropCount: Integer;
  23. FPropList: PPropList;
  24. FName: string;
  25. FIgnoredFields: TStrings;
  26. FWatchedFields: Tstrings;
  27. procedure SetName(const AValue: string);
  28. public
  29. constructor Create; virtual;
  30. destructor Destroy; override;
  31. property PropCount: Integer read FPropCount;
  32. property PropList: PPropList read FPropList;
  33. published
  34. property Name: string read FName write SetName;
  35. property PrimaryKeys: TStrings read FPrimaryKeys;
  36. property IgnoredFields: TStrings read FIgnoredFields;
  37. property WatchedFields: TStrings read FWatchedFields;
  38. end;
  39. { TdSqlBuilder }
  40. TdSqlBuilder = class(TdComponent)
  41. public
  42. procedure Build(out ASql: string;
  43. const ACondition: Boolean = True; AFieldQuote: String = ''); virtual; abstract;
  44. end;
  45. { TdGSqlBuilder }
  46. generic TdGSqlBuilder<T> = class(TdSqlBuilder)
  47. private
  48. FTable: T;
  49. protected
  50. procedure CheckTableName; virtual;
  51. public
  52. procedure SetTable(ATable: T);
  53. end;
  54. { TdGSelectBuilder }
  55. generic TdGSelectBuilder<T> = class(specialize TdGSqlBuilder<T>)
  56. public
  57. class function MakeFields(ATable: T; out AFields: string;
  58. const AIgnoreWildcard: Boolean; AFieldQuote: String = ''): Boolean; virtual;
  59. procedure Build(out ASql: string;
  60. const AIgnoreWildcard: Boolean = True; AFieldQuote: String = ''); override;
  61. end;
  62. { TdGInsertBuilder }
  63. generic TdGInsertBuilder<T> = class(specialize TdGSqlBuilder<T>)
  64. public
  65. class function MakeFields(ATable: T; out AFields, AParams: string;
  66. const AIgnorePrimaryKeys: Boolean; AFieldQuote: String = ''): Boolean; virtual;
  67. procedure Build(out ASql: string;
  68. const AIgnorePrimaryKeys: Boolean = True; AFieldQuote: String = ''); override;
  69. end;
  70. { TdGUpdateBuilder }
  71. generic TdGUpdateBuilder<T> = class(specialize TdGSqlBuilder<T>)
  72. public
  73. class function MakeFields(ATable: T; out AFields, AParams: string;
  74. const AIgnorePrimaryKeys: Boolean; AFieldQuote: String = ''): Boolean; virtual;
  75. procedure Build(out ASql: string;
  76. const AIgnorePrimaryKeys: Boolean = True; AFieldQuote: String = ''); override;
  77. end;
  78. { TdGDeleteBuilder }
  79. generic TdGDeleteBuilder<T> = class(specialize TdGSqlBuilder<T>)
  80. public
  81. class function MakeParams(ATable: T; out AParams: string;
  82. const AIgnoreProperties: Boolean; AFieldQuote: String = ''): Boolean; virtual;
  83. procedure Build(out ASql: string;
  84. const AIgnoreProperties: Boolean = True; AFieldQuote: String = ''); override;
  85. end;
  86. var
  87. dDefaultPrimaryKeyName: ShortString = 'id';
  88. implementation
  89. { TdGTable }
  90. constructor TdGTable.Create;
  91. begin
  92. inherited Create;
  93. FPropCount := GetPropList(PTypeInfo(T.ClassInfo), FPropList);
  94. FPrimaryKeys := TStringList.Create;
  95. FPrimaryKeys.Add(dDefaultPrimaryKeyName);
  96. FIgnoredFields := TStringList.Create;
  97. FWatchedFields := TStringList.Create;
  98. end;
  99. destructor TdGTable.Destroy;
  100. begin
  101. if Assigned(FPropList) then
  102. FreeMem(FPropList);
  103. FPrimaryKeys.Free;
  104. FIgnoredFields.Free;
  105. FWatchedFields.Free;
  106. inherited Destroy;
  107. end;
  108. procedure TdGTable.SetName(const AValue: string);
  109. begin
  110. if Trim(AValue) = '' then
  111. Exit;
  112. if AValue <> FName then
  113. begin
  114. FName := LowerCase(AValue);
  115. end;
  116. end;
  117. { TdGSqlBuilder }
  118. procedure TdGSqlBuilder.CheckTableName;
  119. begin
  120. if Trim(FTable.Name) = '' then
  121. raise EdSqlBuilder.Create('Table name must not be empty.');
  122. end;
  123. procedure TdGSqlBuilder.SetTable(ATable: T);
  124. begin
  125. FTable := ATable;
  126. end;
  127. { TdGSelectBuilder }
  128. class function TdGSelectBuilder.MakeFields(ATable: T; out AFields: string;
  129. const AIgnoreWildcard: Boolean; AFieldQuote: String): Boolean;
  130. var
  131. N: string;
  132. I: Integer;
  133. begin
  134. Result := (ATable <> nil) and Assigned(ATable.PropList);
  135. if not Result then
  136. Exit;
  137. if not AIgnoreWildcard then
  138. begin
  139. AFields := '*';
  140. Exit;
  141. end;
  142. AFields:=EmptyStr;
  143. for I := 0 to Pred(ATable.PropCount) do
  144. begin
  145. N := ATable.PropList^[I]^.Name;
  146. if (ATable.WatchedFields.Count<>0) and (ATable.WatchedFields.IndexOf(N) = -1) then
  147. Continue;
  148. if ATable.IgnoredFields.IndexOf(N) > -1 then
  149. Continue;
  150. AFields += AFieldQuote + N + AFieldQuote + ', ';
  151. end;
  152. SetLength(AFields, Length(AFields) - 2);
  153. AFields := LowerCase(AFields);
  154. end;
  155. procedure TdGSelectBuilder.Build(out ASql: string; const AIgnoreWildcard: Boolean;
  156. AFieldQuote: String);
  157. var
  158. FS: string;
  159. begin
  160. if MakeFields(FTable, FS, AIgnoreWildcard, aFieldQuote) then
  161. begin
  162. CheckTableName;
  163. ASql := 'SELECT ' + FS + ' FROM ' + FTable.Name;
  164. end;
  165. end;
  166. { TdGInsertBuilder }
  167. class function TdGInsertBuilder.MakeFields(ATable: T; out AFields, AParams: string;
  168. const AIgnorePrimaryKeys: Boolean; AFieldQuote: String): Boolean;
  169. var
  170. N: string;
  171. I: Integer;
  172. begin
  173. AFields := '';
  174. AParams := '';
  175. Result := (ATable <> nil) and Assigned(ATable.PropList);
  176. if not Result then
  177. Exit;
  178. for I := 0 to Pred(ATable.PropCount) do
  179. begin
  180. N := ATable.PropList^[I]^.Name;
  181. if (ATable.IgnoredFields.IndexOf(N) > -1) or
  182. (AIgnorePrimaryKeys and (ATable.PrimaryKeys.IndexOf(N) > -1)) or
  183. ((ATable.WatchedFields.Count <> 0) and (ATable.WatchedFields.IndexOf(N) = -1)) then
  184. Continue;
  185. AFields += AFieldQuote + N + AFieldQuote + ', ';
  186. AParams += ':' + N + ', ';
  187. end;
  188. SetLength(AFields, Length(AFields) - 2);
  189. SetLength(AParams, Length(AParams) - 2);
  190. AFields := LowerCase(AFields);
  191. AParams := LowerCase(AParams);
  192. end;
  193. procedure TdGInsertBuilder.Build(out ASql: string; const AIgnorePrimaryKeys: Boolean;
  194. AFieldQuote: String);
  195. var
  196. FS, PS: string;
  197. begin
  198. if MakeFields(FTable, FS, PS, AIgnorePrimaryKeys, AFieldQuote) then
  199. begin
  200. CheckTableName;
  201. ASql := 'INSERT INTO ' + FTable.Name + ' (' + FS + ') ' +
  202. 'VALUES (' + PS + ')';
  203. end;
  204. end;
  205. { TdGUpdateBuilder }
  206. class function TdGUpdateBuilder.MakeFields(ATable: T; out AFields, AParams: string;
  207. const AIgnorePrimaryKeys: Boolean; AFieldQuote: String): Boolean;
  208. var
  209. N, P: string;
  210. I, X: Integer;
  211. begin
  212. AFields := '';
  213. AParams := '';
  214. Result := (ATable <> nil) and Assigned(ATable.PropList);
  215. if not Result then
  216. Exit;
  217. for I := 0 to Pred(ATable.PropCount) do
  218. begin
  219. N := ATable.PropList^[I]^.Name;
  220. X := ATable.PrimaryKeys.IndexOf(N);
  221. if X > -1 then
  222. begin
  223. P := ATable.PrimaryKeys[X];
  224. AParams += AFieldQuote + P + AFieldQuote + ' = :' + P + ' AND ';
  225. if AIgnorePrimaryKeys then
  226. Continue;
  227. end;
  228. if (ATable.WatchedFields.Count <> 0) and (ATable.WatchedFields.IndexOf(N) = -1) then
  229. Continue;
  230. if ATable.IgnoredFields.IndexOf(N) > -1 then
  231. Continue;
  232. AFields += AFieldQuote + N + AFieldQuote + ' = :' + N + ', '
  233. end;
  234. SetLength(AFields, Length(AFields) - 2);
  235. AFields := LowerCase(AFields);
  236. SetLength(AParams, Length(AParams) - 5);
  237. AParams := LowerCase(AParams);
  238. end;
  239. procedure TdGUpdateBuilder.Build(out ASql: string; const AIgnorePrimaryKeys: Boolean;
  240. AFieldQuote: String);
  241. var
  242. FS, PS: string;
  243. begin
  244. if MakeFields(FTable, FS, PS, AIgnorePrimaryKeys, AFieldQuote) then
  245. begin
  246. CheckTableName;
  247. ASQL := 'UPDATE ' + FTable.Name + ' SET ' + FS + ' WHERE ' + PS;
  248. end;
  249. end;
  250. { TdGDeleteBuilder }
  251. class function TdGDeleteBuilder.MakeParams(ATable: T; out AParams: string;
  252. const AIgnoreProperties: Boolean; AFieldQuote: String): Boolean;
  253. var
  254. N, P: string;
  255. I, X: Integer;
  256. begin
  257. AParams := '';
  258. Result := (ATable <> nil) and Assigned(ATable.PropList);
  259. if not Result then
  260. Exit;
  261. for I := 0 to Pred(ATable.PropCount) do
  262. begin
  263. N := ATable.PropList^[I]^.Name;
  264. X := ATable.PrimaryKeys.IndexOf(N);
  265. if X > -1 then
  266. begin
  267. P := ATable.PrimaryKeys[X];
  268. AParams += AFieldQuote + P + AFieldQuote + ' = :' + P + ' AND ';
  269. end
  270. else
  271. begin
  272. if (ATable.WatchedFields.Count <> 0) and (ATable.WatchedFields.IndexOf(N) = -1) then
  273. Continue;
  274. if ATable.IgnoredFields.IndexOf(N) > -1 then
  275. Continue;
  276. if not AIgnoreProperties then
  277. AParams += AFieldQuote + N + AFieldQuote + ' = :' + N + ' AND ';
  278. end;
  279. end;
  280. SetLength(AParams, Length(AParams) - 5);
  281. AParams := LowerCase(AParams);
  282. end;
  283. procedure TdGDeleteBuilder.Build(out ASql: string; const AIgnoreProperties: Boolean;
  284. AFieldQuote: String);
  285. var
  286. PS: string;
  287. begin
  288. if MakeParams(FTable, PS, AIgnoreProperties, AFieldQuote) then
  289. begin
  290. CheckTableName;
  291. ASQL := 'DELETE FROM ' + FTable.Name + ' WHERE ' + PS;
  292. end;
  293. end;
  294. end.