sqldb.pp 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054
  1. {
  2. Copyright (c) 2004 by Joost van der Sluis
  3. SQL database & dataset
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program 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 sqldb;
  11. {$mode objfpc}
  12. {$H+}
  13. {$M+} // ### remove this!!!
  14. interface
  15. uses SysUtils, Classes, DB;
  16. type TSchemaType = (stNoSchema, stTables, stSysTables, stProcedures, stColumns, stProcedureParams, stIndexes, stPackages);
  17. TConnOption = (sqSupportParams);
  18. TConnOptions= set of TConnOption;
  19. type
  20. TSQLConnection = class;
  21. TSQLTransaction = class;
  22. TSQLQuery = class;
  23. TStatementType = (stNone, stSelect, stInsert, stUpdate, stDelete,
  24. stDDL, stGetSegment, stPutSegment, stExecProcedure,
  25. stStartTrans, stCommit, stRollback, stSelectForUpd);
  26. TSQLHandle = Class(TObject)
  27. end;
  28. TSQLCursor = Class(TSQLHandle)
  29. public
  30. FPrepared : Boolean;
  31. FStatementType : TStatementType;
  32. end;
  33. const
  34. StatementTokens : Array[TStatementType] of string = ('(none)', 'select',
  35. 'insert', 'update', 'delete',
  36. 'create', 'get', 'put', 'execute',
  37. 'start','commit','rollback', '?'
  38. );
  39. SQLDelimiterCharacters = [';',',',' ','(',')',#13,#10,#9];
  40. { TSQLConnection }
  41. type
  42. TSQLConnection = class (TDatabase)
  43. private
  44. FPassword : string;
  45. FTransaction : TSQLTransaction;
  46. FUserName : string;
  47. FHostName : string;
  48. FCharSet : string;
  49. FRole : String;
  50. procedure SetTransaction(Value : TSQLTransaction);
  51. protected
  52. FConnOptions : TConnOptions;
  53. function StrToStatementType(s : string) : TStatementType; virtual;
  54. procedure DoInternalConnect; override;
  55. procedure DoInternalDisconnect; override;
  56. function GetAsSQLText(Field : TField) : string; virtual;
  57. function GetHandle : pointer; virtual; abstract;
  58. Function AllocateCursorHandle : TSQLCursor; virtual; abstract;
  59. Function AllocateTransactionHandle : TSQLHandle; virtual; abstract;
  60. procedure PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams); virtual; abstract;
  61. procedure Execute(cursor: TSQLCursor;atransaction:tSQLtransaction; AParams : TParams); virtual; abstract;
  62. function Fetch(cursor : TSQLCursor) : boolean; virtual; abstract;
  63. procedure AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs); virtual; abstract;
  64. procedure CloseStatement(cursor : TSQLCursor); virtual; abstract;
  65. procedure UnPrepareStatement(cursor : TSQLCursor); virtual; abstract;
  66. procedure FreeFldBuffers(cursor : TSQLCursor); virtual; abstract;
  67. function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer) : boolean; virtual; abstract;
  68. function GetTransactionHandle(trans : TSQLHandle): pointer; virtual; abstract;
  69. function Commit(trans : TSQLHandle) : boolean; virtual; abstract;
  70. function RollBack(trans : TSQLHandle) : boolean; virtual; abstract;
  71. function StartdbTransaction(trans : TSQLHandle) : boolean; virtual; abstract;
  72. procedure CommitRetaining(trans : TSQLHandle); virtual; abstract;
  73. procedure RollBackRetaining(trans : TSQLHandle); virtual; abstract;
  74. procedure UpdateIndexDefs(var IndexDefs : TIndexDefs;TableName : string); virtual;
  75. function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; virtual;
  76. function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; virtual;abstract;
  77. Procedure ObtainSQLStatementType(Cursor : TSQLCursor; SQLStr : string);
  78. public
  79. property Handle: Pointer read GetHandle;
  80. destructor Destroy; override;
  81. property ConnOptions: TConnOptions read FConnOptions;
  82. published
  83. property Password : string read FPassword write FPassword;
  84. property Transaction : TSQLTransaction read FTransaction write SetTransaction;
  85. property UserName : string read FUserName write FUserName;
  86. property CharSet : string read FCharSet write FCharSet;
  87. property HostName : string Read FHostName Write FHostName;
  88. property Connected;
  89. Property Role : String read FRole write FRole;
  90. property DatabaseName;
  91. property KeepConnection;
  92. property LoginPrompt;
  93. property Params;
  94. property OnLogin;
  95. end;
  96. { TSQLTransaction }
  97. TCommitRollbackAction = (caNone, caCommit, caCommitRetaining, caRollback,
  98. caRollbackRetaining);
  99. TSQLTransaction = class (TDBTransaction)
  100. private
  101. FTrans : TSQLHandle;
  102. FAction : TCommitRollbackAction;
  103. protected
  104. function GetHandle : Pointer; virtual;
  105. Procedure SetDatabase (Value : TDatabase); override;
  106. public
  107. procedure Commit; virtual;
  108. procedure CommitRetaining; virtual;
  109. procedure Rollback; virtual;
  110. procedure RollbackRetaining; virtual;
  111. procedure StartTransaction; override;
  112. constructor Create(AOwner : TComponent); override;
  113. destructor Destroy; override;
  114. property Handle: Pointer read GetHandle;
  115. procedure EndTransaction; override;
  116. published
  117. property Action : TCommitRollbackAction read FAction write FAction;
  118. property Database;
  119. end;
  120. { TSQLQuery }
  121. TSQLQuery = class (Tbufdataset)
  122. private
  123. FCursor : TSQLCursor;
  124. FUpdateable : boolean;
  125. FTableName : string;
  126. FSQL : TStringList;
  127. FIsEOF : boolean;
  128. FLoadingFieldDefs : boolean;
  129. FIndexDefs : TIndexDefs;
  130. FReadOnly : boolean;
  131. FUpdateMode : TUpdateMode;
  132. FParams : TParams;
  133. FusePrimaryKeyAsKey : Boolean;
  134. FSQLBuf : String;
  135. FFromPart : String;
  136. FWhereStartPos : integer;
  137. FWhereStopPos : integer;
  138. // FSchemaInfo : TSchemaInfo;
  139. procedure CloseStatement;
  140. procedure FreeFldBuffers;
  141. procedure InitUpdates(SQL : string);
  142. function GetIndexDefs : TIndexDefs;
  143. function GetStatementType : TStatementType;
  144. procedure SetIndexDefs(AValue : TIndexDefs);
  145. procedure SetReadOnly(AValue : Boolean);
  146. procedure SetUsePrimaryKeyAsKey(AValue : Boolean);
  147. procedure SetUpdateMode(AValue : TUpdateMode);
  148. procedure OnChangeSQL(Sender : TObject);
  149. procedure Execute;
  150. Procedure ParseSQL(var SQL : string);
  151. protected
  152. // abstract & virtual methods of TBufDataset
  153. function Fetch : boolean; override;
  154. function LoadField(FieldDef : TFieldDef;buffer : pointer) : boolean; override;
  155. // abstract & virtual methods of TDataset
  156. procedure UpdateIndexDefs; override;
  157. procedure SetDatabase(Value : TDatabase); override;
  158. Procedure SetTransaction(Value : TDBTransaction); override;
  159. procedure InternalAddRecord(Buffer: Pointer; AAppend: Boolean); override;
  160. procedure InternalClose; override;
  161. procedure InternalHandleException; override;
  162. procedure InternalInitFieldDefs; override;
  163. procedure InternalOpen; override;
  164. function GetCanModify: Boolean; override;
  165. function ApplyRecUpdate(UpdateKind : TUpdateKind) : boolean; override;
  166. Function IsPrepared : Boolean; virtual;
  167. function GetFieldData(Field: TField; Buffer: Pointer; NativeFormat: Boolean): Boolean; overload; override;
  168. procedure SetFieldData(Field: TField; Buffer: Pointer; NativeFormat: Boolean); overload; override;
  169. procedure SetFiltered(Value: Boolean); override;
  170. procedure SetFilterText(const Value: string); override;
  171. public
  172. procedure Prepare; virtual;
  173. procedure UnPrepare; virtual;
  174. procedure ExecSQL; virtual;
  175. constructor Create(AOwner : TComponent); override;
  176. destructor Destroy; override;
  177. procedure SetSchemaInfo( SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string); virtual;
  178. function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
  179. published
  180. // redeclared data set properties
  181. property Active;
  182. property Filter;
  183. property Filtered;
  184. // property FilterOptions;
  185. property BeforeOpen;
  186. property AfterOpen;
  187. property BeforeClose;
  188. property AfterClose;
  189. property BeforeInsert;
  190. property AfterInsert;
  191. property BeforeEdit;
  192. property AfterEdit;
  193. property BeforePost;
  194. property AfterPost;
  195. property BeforeCancel;
  196. property AfterCancel;
  197. property BeforeDelete;
  198. property AfterDelete;
  199. property BeforeScroll;
  200. property AfterScroll;
  201. property OnCalcFields;
  202. property OnDeleteError;
  203. property OnEditError;
  204. property OnFilterRecord;
  205. property OnNewRecord;
  206. property OnPostError;
  207. property AutoCalcFields;
  208. property Database;
  209. property Transaction;
  210. property ReadOnly : Boolean read FReadOnly write SetReadOnly;
  211. property SQL : TStringlist read FSQL write FSQL;
  212. property IndexDefs : TIndexDefs read GetIndexDefs;
  213. property Params : TParams read FParams write FParams;
  214. property UpdateMode : TUpdateMode read FUpdateMode write SetUpdateMode;
  215. property UsePrimaryKeyAsKey : boolean read FUsePrimaryKeyAsKey write SetUsePrimaryKeyAsKey;
  216. property Prepared : boolean read IsPrepared;
  217. property StatementType : TStatementType read GetStatementType;
  218. // property SchemaInfo : TSchemaInfo read FSchemaInfo default stNoSchema;
  219. end;
  220. implementation
  221. uses dbconst, strutils;
  222. { TSQLConnection }
  223. function TSQLConnection.StrToStatementType(s : string) : TStatementType;
  224. var T : TStatementType;
  225. begin
  226. S:=Lowercase(s);
  227. For t:=stselect to strollback do
  228. if (S=StatementTokens[t]) then
  229. Exit(t);
  230. end;
  231. procedure TSQLConnection.SetTransaction(Value : TSQLTransaction);
  232. begin
  233. if FTransaction<>value then
  234. begin
  235. if Assigned(FTransaction) and FTransaction.Active then
  236. DatabaseError(SErrAssTransaction);
  237. if Assigned(Value) then
  238. Value.Database := Self;
  239. FTransaction := Value;
  240. end;
  241. end;
  242. procedure TSQLConnection.UpdateIndexDefs(var IndexDefs : TIndexDefs;TableName : string);
  243. begin
  244. // Empty abstract
  245. end;
  246. procedure TSQLConnection.DoInternalConnect;
  247. begin
  248. // Empty abstract
  249. end;
  250. procedure TSQLConnection.DoInternalDisconnect;
  251. begin
  252. end;
  253. destructor TSQLConnection.Destroy;
  254. begin
  255. inherited Destroy;
  256. end;
  257. function TSQLConnection.GetAsSQLText(Field : TField) : string;
  258. begin
  259. if not assigned(field) then Result := 'Null'
  260. else case field.DataType of
  261. ftString : Result := '''' + field.asstring + '''';
  262. ftDate : Result := '''' + FormatDateTime('yyyy-mm-dd',Field.AsDateTime) + '''';
  263. ftDateTime : Result := '''' + FormatDateTime('yyyy-mm-dd hh:mm:ss',Field.AsDateTime) + ''''
  264. else
  265. Result := field.asstring;
  266. end; {case}
  267. end;
  268. Procedure TSQLConnection.ObtainSQLStatementType(Cursor : TSQLCursor; SQLStr : string);
  269. Var
  270. L : Integer;
  271. cmt : boolean;
  272. P,PE,PP : PChar;
  273. S : string;
  274. begin
  275. L := Length(SQLstr);
  276. if L=0 then
  277. begin
  278. DatabaseError(SErrNoStatement);
  279. exit;
  280. end;
  281. P:=Pchar(SQLstr);
  282. PP:=P;
  283. Cmt:=False;
  284. While ((P-PP)<L) do
  285. begin
  286. if not (P^ in [' ',#13,#10,#9]) then
  287. begin
  288. if not Cmt then
  289. begin
  290. // Check for comment.
  291. Cmt:=(P^='/') and (((P-PP)<=L) and (P[1]='*'));
  292. if not (cmt) then
  293. Break;
  294. end
  295. else
  296. begin
  297. // Check for end of comment.
  298. Cmt:=Not( (P^='*') and (((P-PP)<=L) and (P[1]='/')) );
  299. If not cmt then
  300. Inc(p);
  301. end;
  302. end;
  303. inc(P);
  304. end;
  305. PE:=P+1;
  306. While ((PE-PP)<L) and (PE^ in ['0'..'9','a'..'z','A'..'Z','_']) do
  307. Inc(PE);
  308. Setlength(S,PE-P);
  309. Move(P^,S[1],(PE-P));
  310. Cursor.FStatementType := StrToStatementType(s);
  311. end;
  312. function TSQLConnection.GetSchemaInfoSQL( SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string;
  313. begin
  314. DatabaseError(SMetadataUnavailable);
  315. end;
  316. { TSQLTransaction }
  317. procedure TSQLTransaction.EndTransaction;
  318. begin
  319. rollback;
  320. end;
  321. function TSQLTransaction.GetHandle: pointer;
  322. begin
  323. Result := (Database as tsqlconnection).GetTransactionHandle(FTrans);
  324. end;
  325. procedure TSQLTransaction.Commit;
  326. begin
  327. if active then
  328. begin
  329. closedatasets;
  330. if (Database as tsqlconnection).commit(FTrans) then
  331. begin
  332. closeTrans;
  333. FreeAndNil(FTrans);
  334. end;
  335. end;
  336. end;
  337. procedure TSQLTransaction.CommitRetaining;
  338. begin
  339. if active then
  340. (Database as tsqlconnection).commitRetaining(FTrans);
  341. end;
  342. procedure TSQLTransaction.Rollback;
  343. begin
  344. if active then
  345. begin
  346. closedatasets;
  347. if (Database as tsqlconnection).RollBack(FTrans) then
  348. begin
  349. CloseTrans;
  350. FreeAndNil(FTrans);
  351. end;
  352. end;
  353. end;
  354. procedure TSQLTransaction.RollbackRetaining;
  355. begin
  356. if active then
  357. (Database as tsqlconnection).RollBackRetaining(FTrans);
  358. end;
  359. procedure TSQLTransaction.StartTransaction;
  360. var db : TSQLConnection;
  361. begin
  362. if Active then
  363. DatabaseError(SErrTransAlreadyActive);
  364. db := (Database as tsqlconnection);
  365. if Db = nil then
  366. DatabaseError(SErrDatabasenAssigned);
  367. if not Db.Connected then
  368. Db.Open;
  369. if not assigned(FTrans) then FTrans := Db.AllocateTransactionHandle;
  370. if Db.StartdbTransaction(FTrans) then OpenTrans;
  371. end;
  372. constructor TSQLTransaction.Create(AOwner : TComponent);
  373. begin
  374. inherited Create(AOwner);
  375. end;
  376. destructor TSQLTransaction.Destroy;
  377. begin
  378. Rollback;
  379. inherited Destroy;
  380. end;
  381. Procedure TSQLTransaction.SetDatabase(Value : TDatabase);
  382. begin
  383. If Value<>Database then
  384. begin
  385. CheckInactive;
  386. If Assigned(Database) then
  387. with Database as TSqlConnection do
  388. if Transaction = self then Transaction := nil;
  389. inherited SetDatabase(Value);
  390. end;
  391. end;
  392. { TSQLQuery }
  393. procedure TSQLQuery.OnChangeSQL(Sender : TObject);
  394. var s : string;
  395. i : integer;
  396. p : pchar;
  397. ParamName : String;
  398. begin
  399. UnPrepare;
  400. if (FSQL <> nil) then
  401. begin
  402. if assigned(FParams) then FParams.Clear;
  403. s := FSQL.Text;
  404. i := posex(':',s);
  405. while i > 0 do
  406. begin
  407. inc(i);
  408. p := @s[i];
  409. repeat
  410. inc(p);
  411. until (p^ in SQLDelimiterCharacters);
  412. if not assigned(FParams) then FParams := TParams.create(self);
  413. ParamName := copy(s,i,p-@s[i]);
  414. if FParams.FindParam(ParamName) = nil then
  415. FParams.CreateParam(ftUnknown, ParamName, ptInput);
  416. i := posex(':',s,i);
  417. end;
  418. end
  419. end;
  420. Procedure TSQLQuery.SetTransaction(Value : TDBTransaction);
  421. begin
  422. UnPrepare;
  423. inherited;
  424. end;
  425. procedure TSQLQuery.SetDatabase(Value : TDatabase);
  426. var db : tsqlconnection;
  427. begin
  428. if (Database <> Value) then
  429. begin
  430. UnPrepare;
  431. db := value as tsqlconnection;
  432. inherited setdatabase(value);
  433. if assigned(value) and (Transaction = nil) and (Assigned(db.Transaction)) then
  434. transaction := Db.Transaction;
  435. end;
  436. end;
  437. procedure TSQLQuery.CloseStatement;
  438. begin
  439. if assigned(FCursor) then
  440. (Database as tsqlconnection).CloseStatement(FCursor);
  441. end;
  442. Function TSQLQuery.IsPrepared : Boolean;
  443. begin
  444. Result := Assigned(FCursor) and FCursor.FPrepared;
  445. end;
  446. function TSQLQuery.GetFieldData(Field: TField; Buffer: Pointer;
  447. NativeFormat: Boolean): Boolean;
  448. begin
  449. Result:=GetFieldData(Field, Buffer);
  450. end;
  451. procedure TSQLQuery.SetFieldData(Field: TField; Buffer: Pointer;
  452. NativeFormat: Boolean);
  453. begin
  454. SetFieldData(Field, Buffer);
  455. end;
  456. procedure TSQLQuery.SetFilterText(const Value: string);
  457. begin
  458. if Filtered then
  459. begin
  460. end;
  461. Inherited SetFilterText(Value);
  462. end;
  463. procedure TSQLQuery.SetFiltered(Value: Boolean);
  464. var S : String;
  465. begin
  466. if (Filtered <> Value) and Active then
  467. begin
  468. CloseStatement;
  469. FIsEOF := False;
  470. inherited internalclose;
  471. s := FSQLBuf;
  472. if Value then
  473. begin
  474. if FWhereStartPos = 0 then
  475. s := s + ' where (' + Filter + ')'
  476. else if FWhereStopPos > 0 then
  477. system.insert(' and ('+Filter+') ',S,FWhereStopPos+1)
  478. else
  479. system.insert(' where ('+Filter+') ',S,FWhereStartPos);
  480. end;
  481. (Database as tsqlconnection).PrepareStatement(Fcursor,(transaction as tsqltransaction),S,FParams);
  482. Execute;
  483. inherited InternalOpen;
  484. First;
  485. inherited SetFiltered(Value);
  486. end;
  487. end;
  488. procedure TSQLQuery.Prepare;
  489. var
  490. db : tsqlconnection;
  491. sqltr : tsqltransaction;
  492. begin
  493. if not IsPrepared then
  494. begin
  495. db := (Database as tsqlconnection);
  496. sqltr := (transaction as tsqltransaction);
  497. if not assigned(Db) then
  498. DatabaseError(SErrDatabasenAssigned);
  499. if not assigned(sqltr) then
  500. DatabaseError(SErrTransactionnSet);
  501. if not Db.Connected then db.Open;
  502. if not sqltr.Active then sqltr.StartTransaction;
  503. if assigned(fcursor) then FreeAndNil(fcursor);
  504. FCursor := Db.AllocateCursorHandle;
  505. FSQLBuf := TrimRight(FSQL.Text);
  506. ParseSQL(FSQLBuf);
  507. Db.PrepareStatement(Fcursor,sqltr,FSQLBuf,FParams);
  508. if (FCursor.FStatementType = stSelect) and not ReadOnly then
  509. InitUpdates(FSQLBuf);
  510. end;
  511. end;
  512. procedure TSQLQuery.UnPrepare;
  513. begin
  514. CheckInactive;
  515. if IsPrepared then (Database as tsqlconnection).UnPrepareStatement(FCursor);
  516. FreeAndNil(FCursor);
  517. end;
  518. procedure TSQLQuery.FreeFldBuffers;
  519. begin
  520. if assigned(FCursor) then (Database as tsqlconnection).FreeFldBuffers(FCursor);
  521. end;
  522. function TSQLQuery.Fetch : boolean;
  523. begin
  524. if not (Fcursor.FStatementType in [stSelect]) then
  525. Exit;
  526. if not FIsEof then FIsEOF := not (Database as tsqlconnection).Fetch(Fcursor);
  527. Result := not FIsEOF;
  528. end;
  529. procedure TSQLQuery.Execute;
  530. begin
  531. (Database as tsqlconnection).execute(Fcursor,Transaction as tsqltransaction, FParams);
  532. end;
  533. function TSQLQuery.LoadField(FieldDef : TFieldDef;buffer : pointer) : boolean;
  534. begin
  535. result := (Database as tSQLConnection).LoadField(FCursor,FieldDef,buffer)
  536. end;
  537. procedure TSQLQuery.InternalAddRecord(Buffer: Pointer; AAppend: Boolean);
  538. begin
  539. // not implemented - sql dataset
  540. end;
  541. procedure TSQLQuery.InternalClose;
  542. begin
  543. FreeFldBuffers;
  544. CloseStatement;
  545. if DefaultFields then
  546. DestroyFields;
  547. FIsEOF := False;
  548. // FRecordSize := 0;
  549. inherited internalclose;
  550. end;
  551. procedure TSQLQuery.InternalHandleException;
  552. begin
  553. end;
  554. procedure TSQLQuery.InternalInitFieldDefs;
  555. begin
  556. if FLoadingFieldDefs then
  557. Exit;
  558. FLoadingFieldDefs := True;
  559. try
  560. FieldDefs.Clear;
  561. (Database as tsqlconnection).AddFieldDefs(fcursor,FieldDefs);
  562. finally
  563. FLoadingFieldDefs := False;
  564. end;
  565. end;
  566. procedure TSQLQuery.ParseSQL(var SQL : string);
  567. type TParsePart = (ppStart,ppSelect,ppWhere,ppFrom,ppOrder,ppComment,ppBogus);
  568. Var
  569. PSQL,CurrentP,
  570. PhraseP, PStatementPart : pchar;
  571. S : string;
  572. ParsePart : TParsePart;
  573. StrLength : Integer;
  574. begin
  575. PSQL:=Pchar(SQL);
  576. ParsePart := ppStart;
  577. CurrentP := PSQL-1;
  578. PhraseP := PSQL;
  579. FWhereStartPos := 0;
  580. FWhereStopPos := 0;
  581. repeat
  582. begin
  583. inc(CurrentP);
  584. if CurrentP^ in [' ',#13,#10,#9,#0,'(',')',';'] then
  585. begin
  586. if (CurrentP-PhraseP > 0) or (CurrentP^ in [';',#0]) then
  587. begin
  588. strLength := CurrentP-PhraseP;
  589. Setlength(S,strLength);
  590. if strLength > 0 then Move(PhraseP^,S[1],(strLength));
  591. s := uppercase(s);
  592. case ParsePart of
  593. ppStart : begin
  594. FCursor.FStatementType := (Database as tsqlconnection).StrToStatementType(s);
  595. if FCursor.FStatementType = stSelect then ParsePart := ppSelect
  596. else break;
  597. PStatementPart := CurrentP;
  598. end;
  599. ppSelect : begin
  600. if s = 'FROM' then
  601. begin
  602. ParsePart := ppFrom;
  603. PhraseP := CurrentP;
  604. PStatementPart := CurrentP;
  605. end;
  606. end;
  607. ppFrom : begin
  608. if (s = 'WHERE') or (s = 'ORDER') or (CurrentP^=#0) or (CurrentP^=';') then
  609. begin
  610. if (s = 'WHERE') then
  611. begin
  612. ParsePart := ppWhere;
  613. StrLength := PhraseP-PStatementPart;
  614. end
  615. else if (s = 'ORDER') then
  616. begin
  617. ParsePart := ppOrder;
  618. StrLength := PhraseP-PStatementPart
  619. end
  620. else
  621. begin
  622. ParsePart := ppBogus;
  623. StrLength := CurrentP-PStatementPart;
  624. end;
  625. Setlength(FFromPart,StrLength);
  626. Move(PStatementPart^,FFromPart[1],(StrLength));
  627. FFrompart := trim(FFrompart);
  628. FWhereStartPos := PStatementPart-PSQL+StrLength+1;
  629. PStatementPart := CurrentP;
  630. end;
  631. end;
  632. ppWhere : begin
  633. if (s = 'ORDER') or (CurrentP^=#0) or (CurrentP^=';') then
  634. begin
  635. ParsePart := ppBogus;
  636. FWhereStartPos := PStatementPart-PSQL;
  637. if s = 'ORDER' then
  638. FWhereStopPos := PhraseP-PSQL+1
  639. else
  640. FWhereStopPos := CurrentP-PSQL+1;
  641. end;
  642. end;
  643. end; {case}
  644. end;
  645. PhraseP := CurrentP+1;
  646. end
  647. end;
  648. until CurrentP^=#0;
  649. if (FWhereStartPos > 0) and (FWhereStopPos > 0) then
  650. begin
  651. system.insert('(',SQL,FWhereStartPos+1);
  652. inc(FWhereStopPos);
  653. system.insert(')',SQL,FWhereStopPos);
  654. end
  655. end;
  656. procedure TSQLQuery.InitUpdates(SQL : string);
  657. begin
  658. if pos(',',FFromPart) > 0 then
  659. FUpdateable := False // select-statements from more then one table are not updateable
  660. else
  661. begin
  662. FUpdateable := True;
  663. FTableName := FFromPart;
  664. end;
  665. end;
  666. procedure TSQLQuery.InternalOpen;
  667. var tel : integer;
  668. f : TField;
  669. s : string;
  670. WasPrepared : boolean;
  671. begin
  672. try
  673. WasPrepared := IsPrepared;
  674. Prepare;
  675. if FCursor.FStatementType in [stSelect] then
  676. begin
  677. Execute;
  678. if not WasPrepared then InternalInitFieldDefs; // if query was prepared before opening, fields are already created
  679. if DefaultFields then
  680. begin
  681. CreateFields;
  682. if FUpdateable and FusePrimaryKeyAsKey then
  683. begin
  684. UpdateIndexDefs;
  685. for tel := 0 to indexdefs.count-1 do {with indexdefs[tel] do}
  686. begin
  687. if ixPrimary in indexdefs[tel].options then
  688. begin
  689. // Todo: If there is more then one field in the key, that must be parsed
  690. s := indexdefs[tel].fields;
  691. F := Findfield(s);
  692. if F <> nil then
  693. F.ProviderFlags := F.ProviderFlags + [pfInKey];
  694. end;
  695. end;
  696. end;
  697. end;
  698. end
  699. else
  700. DatabaseError(SErrNoSelectStatement,Self);
  701. except
  702. on E:Exception do
  703. raise;
  704. end;
  705. inherited InternalOpen;
  706. end;
  707. // public part
  708. procedure TSQLQuery.ExecSQL;
  709. begin
  710. try
  711. Prepare;
  712. Execute;
  713. finally
  714. CloseStatement;
  715. end;
  716. end;
  717. constructor TSQLQuery.Create(AOwner : TComponent);
  718. begin
  719. inherited Create(AOwner);
  720. FSQL := TStringList.Create;
  721. FSQL.OnChange := @OnChangeSQL;
  722. FIndexDefs := TIndexDefs.Create(Self);
  723. FReadOnly := false;
  724. // Delphi has upWhereAll as default, but since strings and oldvalue's don't work yet
  725. // (variants) set it to upWhereKeyOnly
  726. FUpdateMode := upWhereKeyOnly;
  727. FUsePrimaryKeyAsKey := True;
  728. end;
  729. destructor TSQLQuery.Destroy;
  730. begin
  731. if Active then Close;
  732. UnPrepare;
  733. FreeAndNil(FSQL);
  734. FreeAndNil(FIndexDefs);
  735. inherited Destroy;
  736. end;
  737. procedure TSQLQuery.SetReadOnly(AValue : Boolean);
  738. begin
  739. if not Active then FReadOnly := AValue
  740. else
  741. begin
  742. // Just temporary, this should be possible in the future
  743. DatabaseError(SActiveDataset);
  744. end;
  745. end;
  746. procedure TSQLQuery.SetUsePrimaryKeyAsKey(AValue : Boolean);
  747. begin
  748. if not Active then FusePrimaryKeyAsKey := AValue
  749. else
  750. begin
  751. // Just temporary, this should be possible in the future
  752. DatabaseError(SActiveDataset);
  753. end;
  754. end;
  755. Procedure TSQLQuery.UpdateIndexDefs;
  756. begin
  757. if assigned(DataBase) then
  758. (DataBase as TSQLConnection).UpdateIndexDefs(FIndexDefs,FTableName);
  759. end;
  760. function TSQLQuery.ApplyRecUpdate(UpdateKind : TUpdateKind) : boolean;
  761. var
  762. s : string;
  763. procedure UpdateWherePart(var sql_where : string;x : integer);
  764. begin
  765. if (pfInKey in Fields[x].ProviderFlags) or
  766. ((FUpdateMode = upWhereAll) and (pfInWhere in Fields[x].ProviderFlags)) or
  767. ((FUpdateMode = UpWhereChanged) and (pfInWhere in Fields[x].ProviderFlags) and (fields[x].value <> fields[x].oldvalue)) then
  768. begin
  769. // This should be converted to something like GetAsSQLText, but better wait until variants (oldvalue) are working for strings
  770. s := fields[x].oldvalue; // This directly int the line below raises a variant-error
  771. sql_where := sql_where + '(' + fields[x].FieldName + '=' + s + ') and ';
  772. end;
  773. end;
  774. function ModifyRecQuery : string;
  775. var x : integer;
  776. sql_set : string;
  777. sql_where : string;
  778. begin
  779. sql_set := '';
  780. sql_where := '';
  781. for x := 0 to Fields.Count -1 do
  782. begin
  783. UpdateWherePart(sql_where,x);
  784. if (pfInUpdate in Fields[x].ProviderFlags) then
  785. if fields[x].IsNull then // check for null
  786. sql_set := sql_set + fields[x].FieldName + '=' + (Database as TSQLConnection).GetAsSQLText(nil) + ','
  787. else
  788. sql_set := sql_set + fields[x].FieldName + '=' + (Database as TSQLConnection).GetAsSQLText(fields[x]) + ',';
  789. end;
  790. setlength(sql_set,length(sql_set)-1);
  791. setlength(sql_where,length(sql_where)-5);
  792. result := 'update ' + FTableName + ' set ' + sql_set + ' where ' + sql_where;
  793. end;
  794. function InsertRecQuery : string;
  795. var x : integer;
  796. sql_fields : string;
  797. sql_values : string;
  798. begin
  799. sql_fields := '';
  800. sql_values := '';
  801. for x := 0 to Fields.Count -1 do
  802. begin
  803. if not fields[x].IsNull then
  804. begin
  805. sql_fields := sql_fields + fields[x].DisplayName + ',';
  806. sql_values := sql_values + (Database as TSQLConnection).GetAsSQLText(fields[x]) + ',';
  807. end;
  808. end;
  809. setlength(sql_fields,length(sql_fields)-1);
  810. setlength(sql_values,length(sql_values)-1);
  811. result := 'insert into ' + FTableName + ' (' + sql_fields + ') values (' + sql_values + ')';
  812. end;
  813. function DeleteRecQuery : string;
  814. var x : integer;
  815. sql_where : string;
  816. begin
  817. sql_where := '';
  818. for x := 0 to Fields.Count -1 do
  819. UpdateWherePart(sql_where,x);
  820. setlength(sql_where,length(sql_where)-5);
  821. result := 'delete from ' + FTableName + ' where ' + sql_where;
  822. end;
  823. begin
  824. Result := False;
  825. with tsqlquery.Create(nil) do
  826. begin
  827. DataBase := self.Database;
  828. transaction := self.transaction;
  829. sql.clear;
  830. case UpdateKind of
  831. ukModify : s := ModifyRecQuery;
  832. ukInsert : s := InsertRecQuery;
  833. ukDelete : s := DeleteRecQuery;
  834. end; {case}
  835. sql.add(s);
  836. ExecSQL;
  837. Result := true;
  838. Free;
  839. end;
  840. end;
  841. Function TSQLQuery.GetCanModify: Boolean;
  842. begin
  843. if FCursor.FStatementType = stSelect then
  844. Result:= Active and FUpdateable and (not FReadOnly)
  845. else
  846. Result := False;
  847. end;
  848. function TSQLQuery.GetIndexDefs : TIndexDefs;
  849. begin
  850. Result := FIndexDefs;
  851. end;
  852. procedure TSQLQuery.SetIndexDefs(AValue : TIndexDefs);
  853. begin
  854. FIndexDefs := AValue;
  855. end;
  856. procedure TSQLQuery.SetUpdateMode(AValue : TUpdateMode);
  857. begin
  858. FUpdateMode := AValue;
  859. end;
  860. procedure TSQLQuery.SetSchemaInfo( SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string);
  861. begin
  862. SQL.Clear;
  863. SQL.Add((DataBase as tsqlconnection).GetSchemaInfoSQL(SchemaType, SchemaObjectName, SchemaPattern));
  864. end;
  865. function TSQLQuery.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
  866. begin
  867. result := (DataBase as tsqlconnection).CreateBlobStream(Field, Mode);
  868. end;
  869. function TSQLQuery.GetStatementType : TStatementType;
  870. begin
  871. if assigned(FCursor) then Result := FCursor.FStatementType
  872. else Result := stNone;
  873. end;
  874. end.