1 |
tony |
37 |
(* |
2 |
|
|
* IBX For Lazarus (Firebird Express) |
3 |
|
|
* |
4 |
|
|
* The contents of this file are subject to the Initial Developer's |
5 |
|
|
* Public License Version 1.0 (the "License"); you may not use this |
6 |
|
|
* file except in compliance with the License. You may obtain a copy |
7 |
|
|
* of the License here: |
8 |
|
|
* |
9 |
|
|
* http://www.firebirdsql.org/index.php?op=doc&id=idpl |
10 |
|
|
* |
11 |
|
|
* Software distributed under the License is distributed on an "AS |
12 |
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or |
13 |
|
|
* implied. See the License for the specific language governing rights |
14 |
|
|
* and limitations under the License. |
15 |
|
|
* |
16 |
|
|
* The Initial Developer of the Original Code is Tony Whyman. |
17 |
|
|
* |
18 |
|
|
* The Original Code is (C) 2014 Tony Whyman, MWA Software |
19 |
|
|
* (http://www.mwasoftware.co.uk). |
20 |
|
|
* |
21 |
|
|
* All Rights Reserved. |
22 |
|
|
* |
23 |
|
|
* Contributor(s): ______________________________________. |
24 |
|
|
* |
25 |
|
|
*) |
26 |
|
|
program fbsql; |
27 |
|
|
|
28 |
|
|
{$mode objfpc}{$H+} |
29 |
|
|
|
30 |
|
|
uses |
31 |
|
|
{$IFDEF UNIX}{$IFDEF UseCThreads} |
32 |
|
|
cthreads, |
33 |
|
|
{$ENDIF}{$ENDIF} |
34 |
|
|
Classes, SysUtils, CustApp |
35 |
|
|
{ you can add units after this } |
36 |
|
|
,IBDatabase, ibxscript, IBExtract, IBQuery, DB; |
37 |
|
|
|
38 |
|
|
resourcestring |
39 |
|
|
|
40 |
|
|
sUnknownField = 'Unknown Field Type'; |
41 |
|
|
sBadGraphic = 'Unable to generate CSV data for a Graphic Field'; |
42 |
|
|
sBadParadox = 'Unable to generate CSV data for a Paradox OLE Field'; |
43 |
|
|
sBadDBase = 'Unable to generate CSV data for a DBase OLE Field'; |
44 |
|
|
sBadBinary = 'Unable to generate CSV data for a Binary Field'; |
45 |
|
|
sBadCursor = 'Unable to generate CSV data for a Cursor Field'; |
46 |
|
|
|
47 |
|
|
type |
48 |
|
|
|
49 |
|
|
{ TFBSQL } |
50 |
|
|
|
51 |
|
|
TFBSQL = class(TCustomApplication) |
52 |
|
|
private |
53 |
|
|
FIBDatabase: TIBDatabase; |
54 |
|
|
FIBTransaction: TIBTransaction; |
55 |
|
|
FIBXScript: TIBXScript; |
56 |
|
|
FExtract: TIBExtract; |
57 |
|
|
FQuery: TIBQuery; |
58 |
|
|
FSQL: TStringStream; |
59 |
|
|
procedure LogHandler(Sender: TObject; Msg: string); |
60 |
|
|
procedure ErrorLogHandler(Sender: TObject; Msg: string); |
61 |
|
|
procedure HandleSelectSQL(Sender: TObject; SQLText: string); |
62 |
|
|
procedure WriteCSV; |
63 |
|
|
protected |
64 |
|
|
procedure DoRun; override; |
65 |
|
|
procedure ShowException(E: Exception); override; |
66 |
|
|
public |
67 |
|
|
constructor Create(TheOwner: TComponent); override; |
68 |
|
|
destructor Destroy; override; |
69 |
|
|
procedure WriteHelp; virtual; |
70 |
|
|
end; |
71 |
|
|
|
72 |
|
|
{ TFBSQL } |
73 |
|
|
|
74 |
|
|
procedure TFBSQL.LogHandler(Sender: TObject; Msg: string); |
75 |
|
|
begin |
76 |
|
|
writeln( Msg); |
77 |
|
|
end; |
78 |
|
|
|
79 |
|
|
procedure TFBSQL.ErrorLogHandler(Sender: TObject; Msg: string); |
80 |
|
|
begin |
81 |
|
|
writeln(stderr, Msg); |
82 |
|
|
end; |
83 |
|
|
|
84 |
|
|
procedure TFBSQL.HandleSelectSQL(Sender: TObject; SQLText: string); |
85 |
|
|
begin |
86 |
|
|
FQuery.SQL.Text := SQLText; |
87 |
|
|
FQuery.Active := true; |
88 |
|
|
try |
89 |
|
|
WriteCSV; |
90 |
|
|
finally |
91 |
|
|
FQuery.Active := false; |
92 |
|
|
end; |
93 |
|
|
end; |
94 |
|
|
|
95 |
|
|
procedure TFBSQL.WriteCSV; |
96 |
|
|
|
97 |
|
|
procedure WriteQuotedText(Text: string); |
98 |
|
|
var Index: integer; |
99 |
|
|
begin |
100 |
|
|
Index := 1; |
101 |
|
|
while Index <= Length(Text) do |
102 |
|
|
if Text[Index] = '"' then |
103 |
|
|
begin |
104 |
|
|
Insert('"',Text,Index); |
105 |
|
|
Inc(Index,2) |
106 |
|
|
end |
107 |
|
|
else |
108 |
|
|
Inc(Index,1); |
109 |
|
|
write('"' + Text + '"') |
110 |
|
|
end; |
111 |
|
|
|
112 |
|
|
procedure WriteFieldList(Fields: TFields); |
113 |
|
|
var I: integer; |
114 |
|
|
begin |
115 |
|
|
for I := 0 to Fields.Count - 1 do |
116 |
|
|
begin |
117 |
|
|
if I > 0 then write(','); |
118 |
|
|
write(Fields[I].FieldName) |
119 |
|
|
end; |
120 |
|
|
writeln; |
121 |
|
|
end; |
122 |
|
|
|
123 |
|
|
procedure WriteRecord; |
124 |
|
|
var I: integer; |
125 |
|
|
begin |
126 |
|
|
with FQuery do |
127 |
|
|
begin |
128 |
|
|
for I := 0 to FieldCount - 1 do |
129 |
|
|
begin |
130 |
|
|
if I <> 0 then write(','); |
131 |
|
|
case Fields[I].DataType of |
132 |
|
|
ftUnknown: raise Exception.Create(sUnknownField); |
133 |
|
|
ftString: WriteQuotedText(Fields[I].AsString); |
134 |
|
|
ftSmallint, |
135 |
|
|
ftInteger, |
136 |
|
|
ftWord, |
137 |
|
|
ftLargeInt, |
138 |
|
|
ftBoolean: write(Fields[I].DisplayText); |
139 |
|
|
ftFloat, |
140 |
|
|
ftCurrency, |
141 |
|
|
ftFmtBCD, |
142 |
|
|
ftBCD: write(Fields[I].AsString); |
143 |
|
|
ftDate, |
144 |
|
|
ftTime: write(DateTimeToStr(Fields[I].AsDateTime)); |
145 |
|
|
ftDateTime: WriteQuotedText(Fields[I].AsString); |
146 |
|
|
ftBytes, |
147 |
|
|
ftVarBytes, |
148 |
|
|
ftBlob, |
149 |
|
|
ftAutoInc: write(Fields[I].AsString); |
150 |
|
|
ftMemo: WriteQuotedText(Fields[I].AsString); |
151 |
|
|
ftGraphic: raise Exception.Create(sBadGraphic); |
152 |
|
|
ftFmtMemo: WriteQuotedText(Fields[I].AsString); |
153 |
|
|
ftParadoxOle: raise Exception.Create(sBadParadox); |
154 |
|
|
ftDBaseOle: raise Exception.Create(sBadDBase); |
155 |
|
|
ftTypedBinary:raise Exception.Create(sBadBinary); |
156 |
|
|
ftCursor: raise Exception.Create(sBadCursor); |
157 |
|
|
end |
158 |
|
|
end; |
159 |
|
|
writeln; |
160 |
|
|
end; |
161 |
|
|
end; |
162 |
|
|
begin |
163 |
|
|
with FQuery do |
164 |
|
|
begin |
165 |
|
|
WriteFieldList(Fields); |
166 |
|
|
First; |
167 |
|
|
while not EOF do |
168 |
|
|
begin |
169 |
|
|
WriteRecord; |
170 |
|
|
Next |
171 |
|
|
end; |
172 |
|
|
end |
173 |
|
|
end; |
174 |
|
|
|
175 |
|
|
procedure TFBSQL.DoRun; |
176 |
|
|
var |
177 |
|
|
ErrorMsg: String; |
178 |
|
|
SQLFileName: string; |
179 |
|
|
DoExtract: boolean; |
180 |
|
|
i: integer; |
181 |
|
|
begin |
182 |
|
|
writeln(stderr,'fbsql: a non-interactive SQL interpreter for Firebird'); |
183 |
|
|
writeln(stderr,'Copyright (c) MWA Software 2016'); |
184 |
|
|
// quick check parameters |
185 |
|
|
ErrorMsg:=CheckOptions('ahbeufprs',['help','user','pass','role']); |
186 |
|
|
if ErrorMsg<>'' then begin |
187 |
|
|
ShowException(Exception.Create(ErrorMsg)); |
188 |
|
|
Terminate; |
189 |
|
|
Exit; |
190 |
|
|
end; |
191 |
|
|
|
192 |
|
|
// parse parameters |
193 |
|
|
if HasOption('h','help') or (ParamCount = 0) then |
194 |
|
|
begin |
195 |
|
|
WriteHelp; |
196 |
|
|
Terminate; |
197 |
|
|
Exit; |
198 |
|
|
end; |
199 |
|
|
|
200 |
|
|
SQLFileName := ''; |
201 |
|
|
DoExtract := false; |
202 |
|
|
|
203 |
|
|
{Initialise user_name and password from environment if available} |
204 |
|
|
|
205 |
|
|
if GetEnvironmentVariable('ISC_USER') <> '' then |
206 |
|
|
FIBDatabase.Params.Add('user_name=' + GetEnvironmentVariable('ISC_USER')); |
207 |
|
|
|
208 |
|
|
if GetEnvironmentVariable('ISC_PASSWORD') <> '' then |
209 |
|
|
FIBDatabase.Params.Add('password=' + GetEnvironmentVariable('ISC_PASSWORD')); |
210 |
|
|
|
211 |
|
|
{Process Command line options} |
212 |
|
|
|
213 |
|
|
if HasOption('u','user') then |
214 |
|
|
FIBDatabase.Params.Add('user_name=' + GetOptionValue('u','user')); |
215 |
|
|
|
216 |
|
|
if HasOption('p','pass') then |
217 |
|
|
FIBDatabase.Params.Add('password=' + GetOptionValue('p','pass')); |
218 |
|
|
|
219 |
|
|
if HasOption('r','role') then |
220 |
|
|
FIBDatabase.Params.Add('sql_role_name=' + GetOptionValue('r','role')); |
221 |
|
|
|
222 |
|
|
if (ParamCount >= 1) and (ParamStr(ParamCount)[1] <> '-') then |
223 |
|
|
FIBDatabase.DatabaseName := ParamStr(ParamCount) |
224 |
|
|
else |
225 |
|
|
raise Exception.Create('Database Name Missing'); |
226 |
|
|
|
227 |
|
|
if not HasOption('b') then |
228 |
|
|
FIBXScript.StopOnFirstError := false; |
229 |
|
|
|
230 |
|
|
if not HasOption('e') then |
231 |
|
|
FIBXScript.Echo := false; |
232 |
|
|
|
233 |
|
|
if HasOption('a') then |
234 |
|
|
DoExtract := true; |
235 |
|
|
|
236 |
|
|
if HasOption('f') then |
237 |
|
|
SQLFileName := GetOptionValue('f'); |
238 |
|
|
|
239 |
|
|
if HasOption('s') then |
240 |
|
|
begin |
241 |
|
|
FSQL.WriteString(GetOptionValue('s')); |
242 |
|
|
FSQL.Position := 0; |
243 |
|
|
end; |
244 |
|
|
|
245 |
|
|
{Validation} |
246 |
|
|
|
247 |
|
|
if not DoExtract then |
248 |
|
|
begin |
249 |
|
|
if (SQLFileName = '') and (FSQL.DataString = '') then |
250 |
|
|
raise Exception.Create('An SQL File must be provided'); |
251 |
|
|
|
252 |
|
|
if (FSQL.DataString <> '') and (SQLFileName <> '') then |
253 |
|
|
raise Exception.Create('An SQL Script File and text cannot be simulateously requested'); |
254 |
|
|
|
255 |
|
|
if (FSQL.DataString = '') and not FileExists(SQLFileName) then |
256 |
|
|
raise Exception.CreateFmt('SQL File "%s" not found!',[SQLFileName]); |
257 |
|
|
|
258 |
|
|
end; |
259 |
|
|
|
260 |
|
|
if DoExtract and ((SQLFileName <> '') or (FSQL.DataString <> '')) then |
261 |
|
|
raise Exception.Create('Extract and script execution cannot be simulateously requested'); |
262 |
|
|
|
263 |
|
|
{This is where it all happens} |
264 |
|
|
|
265 |
|
|
FIBDatabase.Connected := true; |
266 |
|
|
try |
267 |
|
|
if DoExtract then |
268 |
|
|
begin |
269 |
|
|
FExtract.ExtractObject(eoDatabase); |
270 |
|
|
for i := 0 to FExtract.Items.Count - 1 do |
271 |
|
|
writeln(FExtract.Items[i]); |
272 |
|
|
end |
273 |
|
|
else |
274 |
|
|
if FSQL.DataString = '' then |
275 |
|
|
FIBXScript.PerformUpdate(SQLFileName,true) |
276 |
|
|
else |
277 |
|
|
FIBXScript.PerformUpdate(FSQL,true); |
278 |
|
|
finally |
279 |
|
|
FIBDatabase.Connected := false; |
280 |
|
|
end; |
281 |
|
|
|
282 |
|
|
|
283 |
|
|
// stop program loop |
284 |
|
|
Terminate; |
285 |
|
|
end; |
286 |
|
|
|
287 |
|
|
procedure TFBSQL.ShowException(E: Exception); |
288 |
|
|
begin |
289 |
|
|
writeln(stderr,'Error: ' + E.Message); |
290 |
|
|
end; |
291 |
|
|
|
292 |
|
|
constructor TFBSQL.Create(TheOwner: TComponent); |
293 |
|
|
begin |
294 |
|
|
inherited Create(TheOwner); |
295 |
|
|
StopOnException:=True; |
296 |
|
|
FSQL := TStringStream.Create(''); |
297 |
|
|
|
298 |
|
|
{ Create Components } |
299 |
|
|
FIBDatabase := TIBDatabase.Create(self); |
300 |
|
|
FIBTransaction := TIBTransaction.Create(self); |
301 |
|
|
FIBTransaction.DefaultDatabase := FIBDatabase; |
302 |
|
|
FIBXScript := TIBXScript.Create(self); |
303 |
|
|
FIBXScript.Database := FIBDatabase; |
304 |
|
|
FIBXScript.Transaction := FIBTransaction; |
305 |
|
|
FIBXScript.OnOutputLog := @LogHandler; |
306 |
|
|
FIBXScript.OnErrorLog := @ErrorLogHandler; |
307 |
|
|
FIBXScript.OnSelectSQL := @HandleSelectSQL; |
308 |
|
|
FExtract := TIBExtract.Create(self); |
309 |
|
|
FExtract.Database := FIBDatabase; |
310 |
|
|
FExtract.Transaction := FIBTransaction; |
311 |
|
|
FQuery := TIBQuery.Create(self); |
312 |
tony |
45 |
FQuery.AllowAutoActivateTransaction := true; |
313 |
tony |
37 |
FQuery.Database := FIBDatabase; |
314 |
|
|
FQuery.Transaction := FIBTransaction; |
315 |
|
|
|
316 |
|
|
FIBTransaction.Params.Add('concurrency'); |
317 |
|
|
FIBTransaction.Params.Add('wait'); |
318 |
|
|
FIBDatabase.Params.Add('lc_ctype=UTF8'); |
319 |
|
|
|
320 |
|
|
end; |
321 |
|
|
|
322 |
|
|
destructor TFBSQL.Destroy; |
323 |
|
|
begin |
324 |
|
|
if assigned(FSQL) then FSQL.Free; |
325 |
|
|
inherited Destroy; |
326 |
|
|
end; |
327 |
|
|
|
328 |
|
|
procedure TFBSQL.WriteHelp; |
329 |
|
|
begin |
330 |
|
|
writeln(stderr,'Usage: ',ExtractFileName(ExeName),' <options> <database name>'); |
331 |
|
|
writeln(stderr,'Options:'); |
332 |
|
|
writeln(stderr,'-a write database metadata to stdout'); |
333 |
|
|
writeln(stderr,'-b stop on first error'); |
334 |
|
|
writeln(stderr,'-e echo sql statements to stdout'); |
335 |
|
|
writeln(stderr,'-f <filename> execute SQL script from file'); |
336 |
|
|
writeln(stderr,'-h show this information'); |
337 |
|
|
writeln(stderr,'-p <password> provide password on command line (insecure)'); |
338 |
|
|
writeln(stderr,'-r <rolename> open database with this rolename'); |
339 |
|
|
writeln(stderr,'-s <sql> Execute SQL text'); |
340 |
|
|
writeln(stderr,'-u <username> open database with this username (defaults to SYSDBA)'); |
341 |
|
|
writeln; |
342 |
|
|
writeln(stderr,'Environment Variables:'); |
343 |
|
|
writeln(stderr,'ISC_USER Login user Name'); |
344 |
|
|
writeln(stderr,'ISC_PASSWORD Login password'); |
345 |
|
|
end; |
346 |
|
|
|
347 |
|
|
var |
348 |
|
|
Application: TFBSQL; |
349 |
|
|
begin |
350 |
|
|
Application:=TFBSQL.Create(nil); |
351 |
|
|
Application.Title:='fbsql'; |
352 |
|
|
Application.Run; |
353 |
|
|
Application.Free; |
354 |
|
|
end. |
355 |
|
|
|