PageRenderTime 60ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/fbclone.dpr

http://fbclone.googlecode.com/
Pascal | 317 lines | 263 code | 35 blank | 19 comment | 62 complexity | d9a0b0ef3405681abb5ea7d6473f8ed3 MD5 | raw file
  1. (*
  2. * The contents of this file are subject to the
  3. * Initial Developer's Public License Version 1.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License from the Firebird Project website,
  6. * at http://www.firebirdsql.org/index.php?op=doc&id=idpl.
  7. *
  8. * Software distributed under the License is distributed on an "AS IS" basis,
  9. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  10. * for the specific language governing rights and limitations under the License.
  11. *
  12. * The Original Code was created by Pierre Yager and Henri Gourvest.
  13. *)
  14. program fbclone;
  15. {$APPTYPE CONSOLE}
  16. {$R *.res}
  17. uses
  18. Windows,
  19. SysUtils,
  20. Classes,
  21. Generics.Collections,
  22. uib,
  23. uiblib,
  24. uibase,
  25. uibmetadata,
  26. uibconst,
  27. console.getopts in 'console.getopts.pas',
  28. fbclone.cloner in 'fbclone.cloner.pas',
  29. {$IFDEF ENABLE_BENCHMARK}
  30. fbclone.benchmark in 'fbclone.benchmark.pas',
  31. {$ENDIF}
  32. fbclone.database in 'fbclone.database.pas',
  33. fbclone.logger in 'fbclone.logger.pas';
  34. procedure BackupRepairFile(const repair_file_name: string);
  35. var
  36. repair_file_ext: string;
  37. backup_file_name: string;
  38. begin
  39. if FileExists(repair_file_name) then
  40. begin
  41. repair_file_ext := ExtractFileExt(repair_file_name);
  42. if repair_file_ext <> '' then
  43. Insert('~', repair_file_ext, 2);
  44. backup_file_name := ChangeFileExt(repair_file_name, repair_file_ext);
  45. if FileExists(backup_file_name) then
  46. DeleteFile(backup_file_name);
  47. RenameFile(repair_file_name, backup_file_name);
  48. end;
  49. end;
  50. procedure RegisterExcludedTables(Cloner: TCloner; const TablesList: string);
  51. var
  52. L: TStringList;
  53. S: String;
  54. begin
  55. L := TStringList.Create;
  56. try
  57. L.Delimiter := ',';
  58. L.DelimitedText := TablesList;
  59. for S in L do
  60. Cloner.AddExcludedTable(S);
  61. finally
  62. L.Free;
  63. end;
  64. end;
  65. var
  66. GO: TGetOpt;
  67. O: POption;
  68. P: TPair<POption, string>;
  69. src, tgt: TDatabase;
  70. target_charset, read_charset, write_charset: string;
  71. opts: TClonerOptions;
  72. dump_file, repair_file: string;
  73. commit_interval: integer;
  74. page_size: Integer;
  75. excluded_tables: string;
  76. c: TCloner;
  77. l: ILogger;
  78. begin
  79. opts := [];
  80. commit_interval := 10000;
  81. target_charset := '';
  82. read_charset := '';
  83. write_charset := '';
  84. dump_file := '';
  85. page_size := 0;
  86. l := TConsoleLogger.Create;
  87. GO := TGetOpt.Create;
  88. try
  89. try
  90. GO.RegisterFlag('h', 'help', '', 'Show this help message', false);
  91. GO.RegisterFlag('v', 'verbose', '', 'Show details', false);
  92. GO.RegisterFlag('po', 'pump-only', '', 'Only pump data from source database into target database (source database and target database must share the same metadata structure)', false);
  93. GO.RegisterFlag('e', 'empty-tables', '', 'Empty tables before data pump', false);
  94. GO.RegisterSwitch('d', 'dump', 'file', 'Dump SQL script into file', false);
  95. GO.RegisterSwitch('rd', 'repair-dump', 'file', 'Trace errors and SQL into a repair.sql file', false);
  96. GO.RegisterSwitch('ps', 'page-size', 'page size', 'Overrides target database page size', false);
  97. GO.RegisterSwitch('s', 'source', 'database', 'Source database connection string', true);
  98. GO.RegisterSwitch('su', 'source-user', 'user', 'User name used to connect source database', false);
  99. GO.RegisterSwitch('sp', 'source-password', 'password', 'Password used to connect source database', false);
  100. GO.RegisterSwitch('sl', 'source-library', 'library', 'Client Library used to connect source database', false);
  101. GO.RegisterSwitch('t', 'target', 'database', 'Target database connection string', true);
  102. GO.RegisterSwitch('tu', 'target-user', 'user', 'User name used to connect target database', false);
  103. GO.RegisterSwitch('tp', 'target-password', 'password', 'Password used to connect target dat base', false);
  104. GO.RegisterSwitch('tl', 'target-library', 'library', 'Client Library used to connect target database', false);
  105. GO.RegisterSwitch('tc', 'target-charset', 'charset', 'Target database default character set (default: source charset)', false);
  106. GO.RegisterSwitch('rc', 'read-charset', 'charset', 'Character set to read from source database (default: source charset)', false);
  107. GO.RegisterSwitch('wc', 'write-charset', 'charset', 'Character set to write into target database (default: source charset)', false);
  108. GO.RegisterSwitch('xt', 'exclude-table', 'list', 'Comma separated list of tables to exclude from data pump', false);
  109. GO.RegisterSwitch('u', 'user', 'user', 'User name used to connect both source and target databases', false);
  110. GO.RegisterSwitch('p', 'password', 'password', 'Password used to connect both source and target databases', false);
  111. GO.RegisterSwitch('l', 'library', 'library', 'Client Library used to connect both source and target databases', false);
  112. GO.RegisterFlag('f', 'failsafe', '', 'Commit transaction every record (same as using -ci 1)', false);
  113. GO.RegisterSwitch('ci', 'commit-interval', 'number', 'Commit transaction every <number> record', false);
  114. GO.Parse;
  115. if GO.Flag['h'] then
  116. begin
  117. l.Info(GO.PrintSyntax);
  118. l.Info;
  119. l.Info(GO.PrintHelp);
  120. Exit;
  121. end;
  122. if not GO.Validate then
  123. begin
  124. l.Error('Missing parameters on command line:');
  125. for O in GO.Missing do
  126. l.Error(' ' + O.ToShortSyntax);
  127. l.Error;
  128. l.Error(GO.PrintSyntax);
  129. Halt(1);
  130. end;
  131. if GO.Flag['u'] and (GO.Flag['su'] or GO.Flag['tu']) then
  132. begin
  133. l.Error('Conflict between parameters on command line:');
  134. l.Error(' Flags -tu and -su cannot be used with -u');
  135. l.Error;
  136. l.Error(GO.PrintSyntax);
  137. Halt(1);
  138. end;
  139. if GO.Flag['p'] and (GO.Flag['sp'] or GO.Flag['tp']) then
  140. begin
  141. l.Error('Conflict between parameters on command line:');
  142. l.Error(' Flags -tp and -sp cannot be used with -p');
  143. l.Error;
  144. l.Error(GO.PrintSyntax);
  145. Halt(1);
  146. end;
  147. if GO.Flag['l'] and (GO.Flag['sl'] or GO.Flag['tl']) then
  148. begin
  149. l.Error('Conflict between parameters on command line:');
  150. l.Error(' Flags -tl and -sl cannot be used with -l');
  151. l.Error;
  152. l.Error(GO.PrintSyntax);
  153. Halt(1);
  154. end;
  155. if GO.Flag['po'] and GO.Flag['ps'] then
  156. begin
  157. l.Error('Useless flag on command line:');
  158. l.Error(' The flag -ps (Page Size) will be ignored if -po (Pump Only Mode) is specified');
  159. l.Error;
  160. end;
  161. if GO.Flag['po'] and GO.Flag['tc'] then
  162. begin
  163. l.Error('Useless flag on command line:');
  164. l.Error(' The flag -tc (Target Character Set) will be ignored if -po (Pump Only Mode) is specified');
  165. l.Error;
  166. end;
  167. if GO.Flag['e'] and (not GO.Flag['po']) then
  168. begin
  169. l.Error('Useless flag on command line:');
  170. l.Error(' The flag -e (Empty Tables) will be ignored if -po (Pump Only Mode) is not specified');
  171. l.Error;
  172. end;
  173. if GO.Flag['f'] and GO.Flag['ci'] then
  174. begin
  175. l.Error('Useless flag on command line:');
  176. l.Error(' The flag -ci (Commit Interval) will be ignored if -f (Failsafe Mode) is specified');
  177. l.Error;
  178. end;
  179. for P in GO.Parameters do
  180. begin
  181. if P.Key^.Short = 's' then
  182. src.ConnectionString := P.Value
  183. else if P.Key^.Short = 'u' then
  184. begin
  185. src.Username := P.Value;
  186. tgt.Username := P.Value;
  187. end
  188. else if P.Key^.Short = 'p' then
  189. begin
  190. src.Password := P.Value;
  191. tgt.Password := P.Value;
  192. end
  193. else if P.Key^.Short = 'l' then
  194. begin
  195. src.LibraryFileName := P.Value;
  196. tgt.LibraryFileName := P.Value;
  197. end
  198. else if P.Key^.Short = 'su' then
  199. src.Username := P.Value
  200. else if P.Key^.Short = 'sp' then
  201. src.Password := P.Value
  202. else if P.Key^.Short = 'sl' then
  203. src.LibraryFileName := P.Value
  204. else if P.Key^.Short = 't' then
  205. tgt.ConnectionString := P.Value
  206. else if P.Key^.Short = 'tu' then
  207. tgt.Username := P.Value
  208. else if P.Key^.Short = 'tp' then
  209. tgt.Password := P.Value
  210. else if P.Key^.Short = 'tl' then
  211. tgt.LibraryFileName := P.Value
  212. else if P.Key^.Short = 'tc' then
  213. target_charset := P.Value
  214. else if P.Key^.Short = 'rc' then
  215. read_charset := P.Value
  216. else if P.Key^.Short = 'wc' then
  217. write_charset := P.Value
  218. else if P.Key^.Short = 'v' then
  219. Include(opts, coVerbose)
  220. else if P.Key^.Short = 'd' then
  221. dump_file := P.Value
  222. else if P.Key^.Short = 'rd' then
  223. repair_file := P.Value
  224. else if P.Key^.Short = 'po' then
  225. Include(opts, coPumpOnly)
  226. else if P.Key^.Short = 'e' then
  227. Include(opts, coEmptyTables)
  228. else if P.Key^.Short = 'f' then
  229. Include(opts, coFailSafe)
  230. else if P.Key^.Short = 'ci' then
  231. commit_interval := StrToInt(P.Value)
  232. else if P.Key^.Short = 'ps' then
  233. page_size := StrToInt(P.Value)
  234. else if P.Key^.Short = 'xt' then
  235. excluded_tables := P.Value
  236. end;
  237. MapEnvironment(src);
  238. MapEnvironment(tgt);
  239. if repair_file <> '' then
  240. BackupRepairFile(repair_file);
  241. except
  242. on E: Exception do
  243. begin
  244. l.Error('Error on command line ' + E.Message);
  245. l.Error(GO.PrintSyntax);
  246. Halt(1);
  247. end;
  248. end;
  249. finally
  250. GO.Free;
  251. end;
  252. try
  253. c := TCloner.Create;
  254. try
  255. c.Logger := l;
  256. c.PageSize := page_size;
  257. c.TargetCharset := target_charset;
  258. c.ReadCharset := read_charset;
  259. c.WriteCharset := write_charset;
  260. c.Options := opts;
  261. c.CommitInterval := commit_interval;
  262. c.DumpFile := dump_file;
  263. c.RepairFile := repair_file;
  264. RegisterExcludedTables(c, excluded_tables);
  265. if not c.Clone(src, tgt) then
  266. Halt(1);
  267. finally
  268. c.Free;
  269. end;
  270. {$IFDEF DEBUG}
  271. WriteLn;
  272. WriteLn('[DEBUG] Press a key to terminate');
  273. ReadLn;
  274. {$ENDIF}
  275. except
  276. on E: Exception do
  277. begin
  278. l.Error('General exception ' + E.Message);
  279. Halt(1);
  280. end;
  281. end;
  282. end.