sqldb.pp 28 KB

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