pqconnection.pp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. unit pqconnection;
  2. {$mode objfpc}{$H+}
  3. {$Define LinkDynamically}
  4. interface
  5. uses
  6. Classes, SysUtils, sqldb, db, dbconst,
  7. {$IfDef LinkDynamically}
  8. postgres3dyn;
  9. {$Else}
  10. postgres3;
  11. {$EndIf}
  12. type
  13. TPQTrans = Class(TSQLHandle)
  14. protected
  15. TransactionHandle : PPGConn;
  16. end;
  17. TPQCursor = Class(TSQLCursor)
  18. protected
  19. Statement : string;
  20. tr : Pointer;
  21. nFields : integer;
  22. res : PPGresult;
  23. BaseRes : PPGresult;
  24. Nr : string;
  25. end;
  26. TPQConnection = class (TSQLConnection)
  27. private
  28. FCursorCount : word;
  29. FConnectString : string;
  30. FSQLDatabaseHandle : pointer;
  31. function TranslateFldType(Type_Oid : integer) : TFieldType;
  32. protected
  33. procedure DoInternalConnect; override;
  34. procedure DoInternalDisconnect; override;
  35. function GetHandle : pointer; override;
  36. Function AllocateCursorHandle : TSQLCursor; override;
  37. Function AllocateTransactionHandle : TSQLHandle; override;
  38. procedure CloseStatement(cursor : TSQLCursor); override;
  39. procedure PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams); override;
  40. procedure FreeFldBuffers(cursor : TSQLCursor); override;
  41. procedure Execute(cursor: TSQLCursor;atransaction:tSQLtransaction; AParams : TParams); override;
  42. procedure AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs); override;
  43. function Fetch(cursor : TSQLCursor) : boolean; override;
  44. procedure UnPrepareStatement(cursor : TSQLCursor); override;
  45. function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer) : boolean; override;
  46. function GetTransactionHandle(trans : TSQLHandle): pointer; override;
  47. function RollBack(trans : TSQLHandle) : boolean; override;
  48. function Commit(trans : TSQLHandle) : boolean; override;
  49. procedure CommitRetaining(trans : TSQLHandle); override;
  50. function StartdbTransaction(trans : TSQLHandle) : boolean; override;
  51. procedure RollBackRetaining(trans : TSQLHandle); override;
  52. procedure UpdateIndexDefs(var IndexDefs : TIndexDefs;TableName : string); override;
  53. public
  54. constructor Create(AOwner : TComponent); override;
  55. published
  56. property DatabaseName;
  57. property KeepConnection;
  58. property LoginPrompt;
  59. property Params;
  60. property OnLogin;
  61. end;
  62. implementation
  63. ResourceString
  64. SErrRollbackFailed = 'Rollback transaction failed';
  65. SErrCommitFailed = 'Commit transaction failed';
  66. SErrConnectionFailed = 'Connection to database failed';
  67. SErrTransactionFailed = 'Start of transacion failed';
  68. SErrClearSelection = 'Clear of selection failed';
  69. SErrExecuteFailed = 'Execution of query failed';
  70. SErrFieldDefsFailed = 'Can not extract field information from query';
  71. SErrFetchFailed = 'Fetch of data failed';
  72. SErrNoDatabaseName = 'Database connect string (DatabaseName) not filled in!';
  73. SErrPrepareFailed = 'Preparation of query failed.';
  74. const Oid_Bool = 16;
  75. Oid_Text = 25;
  76. Oid_Name = 19;
  77. Oid_Int8 = 20;
  78. Oid_int2 = 21;
  79. Oid_Int4 = 23;
  80. Oid_Float4 = 700;
  81. Oid_Float8 = 701;
  82. Oid_bpchar = 1042;
  83. Oid_varchar = 1043;
  84. Oid_timestamp = 1114;
  85. oid_date = 1082;
  86. oid_time = 1083;
  87. oid_numeric = 1700;
  88. constructor TPQConnection.Create(AOwner : TComponent);
  89. begin
  90. inherited;
  91. FConnOptions := FConnOptions + [sqSupportParams];
  92. end;
  93. function TPQConnection.GetTransactionHandle(trans : TSQLHandle): pointer;
  94. begin
  95. Result := (trans as TPQtrans).TransactionHandle;
  96. end;
  97. function TPQConnection.RollBack(trans : TSQLHandle) : boolean;
  98. var
  99. res : PPGresult;
  100. tr : TPQTrans;
  101. begin
  102. result := false;
  103. tr := trans as TPQTrans;
  104. res := PQexec(tr.TransactionHandle, 'ROLLBACK');
  105. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  106. begin
  107. PQclear(res);
  108. result := false;
  109. DatabaseError(SErrRollbackFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
  110. end
  111. else
  112. begin
  113. PQclear(res);
  114. PQFinish(tr.TransactionHandle);
  115. result := true;
  116. end;
  117. end;
  118. function TPQConnection.Commit(trans : TSQLHandle) : boolean;
  119. var
  120. res : PPGresult;
  121. tr : TPQTrans;
  122. begin
  123. result := false;
  124. tr := trans as TPQTrans;
  125. res := PQexec(tr.TransactionHandle, 'COMMIT');
  126. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  127. begin
  128. PQclear(res);
  129. result := false;
  130. DatabaseError(SErrCommitFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
  131. end
  132. else
  133. begin
  134. PQclear(res);
  135. PQFinish(tr.TransactionHandle);
  136. result := true;
  137. end;
  138. end;
  139. function TPQConnection.StartdbTransaction(trans : TSQLHandle) : boolean;
  140. var
  141. res : PPGresult;
  142. tr : TPQTrans;
  143. msg : string;
  144. begin
  145. result := false;
  146. tr := trans as TPQTrans;
  147. tr.TransactionHandle := PQconnectdb(pchar(FConnectString));
  148. if (PQstatus(tr.TransactionHandle) = CONNECTION_BAD) then
  149. begin
  150. result := false;
  151. PQFinish(tr.TransactionHandle);
  152. DatabaseError(SErrConnectionFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
  153. end
  154. else
  155. begin
  156. res := PQexec(tr.TransactionHandle, 'BEGIN');
  157. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  158. begin
  159. result := false;
  160. PQclear(res);
  161. msg := PQerrorMessage(tr.transactionhandle);
  162. PQFinish(tr.TransactionHandle);
  163. DatabaseError(sErrTransactionFailed + ' (PostgreSQL: ' + msg + ')',self);
  164. end
  165. else
  166. begin
  167. PQclear(res);
  168. result := true;
  169. end;
  170. end;
  171. end;
  172. procedure TPQConnection.RollBackRetaining(trans : TSQLHandle);
  173. var
  174. res : PPGresult;
  175. tr : TPQTrans;
  176. msg : string;
  177. begin
  178. tr := trans as TPQTrans;
  179. res := PQexec(tr.TransactionHandle, 'ROLLBACK');
  180. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  181. begin
  182. PQclear(res);
  183. DatabaseError(SErrRollbackFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
  184. end
  185. else
  186. begin
  187. PQclear(res);
  188. res := PQexec(tr.TransactionHandle, 'BEGIN');
  189. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  190. begin
  191. PQclear(res);
  192. msg := PQerrorMessage(tr.transactionhandle);
  193. PQFinish(tr.TransactionHandle);
  194. DatabaseError(sErrTransactionFailed + ' (PostgreSQL: ' + msg + ')',self);
  195. end
  196. else
  197. PQclear(res);
  198. end;
  199. end;
  200. procedure TPQConnection.CommitRetaining(trans : TSQLHandle);
  201. var
  202. res : PPGresult;
  203. tr : TPQTrans;
  204. msg : string;
  205. begin
  206. tr := trans as TPQTrans;
  207. res := PQexec(tr.TransactionHandle, 'COMMIT');
  208. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  209. begin
  210. PQclear(res);
  211. DatabaseError(SErrCommitFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
  212. end
  213. else
  214. begin
  215. PQclear(res);
  216. res := PQexec(tr.TransactionHandle, 'BEGIN');
  217. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  218. begin
  219. PQclear(res);
  220. msg := PQerrorMessage(tr.transactionhandle);
  221. PQFinish(tr.TransactionHandle);
  222. DatabaseError(sErrTransactionFailed + ' (PostgreSQL: ' + msg + ')',self);
  223. end
  224. else
  225. PQclear(res);
  226. end;
  227. end;
  228. procedure TPQConnection.DoInternalConnect;
  229. var msg : string;
  230. begin
  231. {$IfDef LinkDynamically}
  232. InitialisePostgres3;
  233. {$EndIf}
  234. inherited dointernalconnect;
  235. if (DatabaseName = '') then
  236. DatabaseError(SErrNoDatabaseName,self);
  237. FConnectString := '';
  238. if (UserName <> '') then FConnectString := FConnectString + ' user=''' + UserName + '''';
  239. if (Password <> '') then FConnectString := FConnectString + ' password=''' + Password + '''';
  240. if (HostName <> '') then FConnectString := FConnectString + ' host=''' + HostName + '''';
  241. if (DatabaseName <> '') then FConnectString := FConnectString + ' dbname=''' + DatabaseName + '''';
  242. FSQLDatabaseHandle := PQconnectdb(pchar(FConnectString));
  243. if (PQstatus(FSQLDatabaseHandle) = CONNECTION_BAD) then
  244. begin
  245. msg := PQerrorMessage(FSQLDatabaseHandle);
  246. dointernaldisconnect;
  247. DatabaseError(sErrConnectionFailed + ' (PostgreSQL: ' + msg + ')',self);
  248. end;
  249. end;
  250. procedure TPQConnection.DoInternalDisconnect;
  251. begin
  252. PQfinish(FSQLDatabaseHandle);
  253. {$IfDef LinkDynamically}
  254. ReleasePostgres3;
  255. {$EndIf}
  256. end;
  257. function TPQConnection.TranslateFldType(Type_Oid : integer) : TFieldType;
  258. begin
  259. case Type_Oid of
  260. Oid_varchar,Oid_bpchar,
  261. Oid_name : Result := ftstring;
  262. Oid_text : REsult := ftmemo;
  263. Oid_int8 : Result := ftLargeInt;
  264. Oid_int4 : Result := ftInteger;
  265. Oid_int2 : Result := ftSmallInt;
  266. Oid_Float4 : Result := ftFloat;
  267. Oid_Float8 : Result := ftFloat;
  268. Oid_TimeStamp : Result := ftDateTime;
  269. Oid_Date : Result := ftDate;
  270. Oid_Time : Result := ftTime;
  271. Oid_Bool : Result := ftBoolean;
  272. Oid_Numeric : Result := ftBCD;
  273. end;
  274. end;
  275. Function TPQConnection.AllocateCursorHandle : TSQLCursor;
  276. begin
  277. result := TPQCursor.create;
  278. end;
  279. Function TPQConnection.AllocateTransactionHandle : TSQLHandle;
  280. begin
  281. result := TPQTrans.create;
  282. end;
  283. procedure TPQConnection.PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams);
  284. const TypeStrings : array[TFieldType] of string =
  285. (
  286. 'Unknown',
  287. 'text',
  288. 'int',
  289. 'int',
  290. 'int',
  291. 'bool',
  292. 'float',
  293. 'numeric',
  294. 'numeric',
  295. 'date',
  296. 'time',
  297. 'datetime',
  298. 'Unknown',
  299. 'Unknown',
  300. 'Unknown',
  301. 'Unknown',
  302. 'Unknown',
  303. 'Unknown',
  304. 'Unknown',
  305. 'Unknown',
  306. 'Unknown',
  307. 'Unknown',
  308. 'Unknown',
  309. 'Unknown',
  310. 'Unknown',
  311. 'int',
  312. 'Unknown',
  313. 'Unknown',
  314. 'Unknown',
  315. 'Unknown',
  316. 'Unknown',
  317. 'Unknown',
  318. 'Unknown',
  319. 'Unknown',
  320. 'Unknown',
  321. 'Unknown',
  322. 'Unknown',
  323. 'Unknown'
  324. );
  325. var s : string;
  326. i : integer;
  327. begin
  328. ObtainSQLStatementType(cursor,buf);
  329. with (cursor as TPQCursor) do
  330. begin
  331. FPrepared := False;
  332. nr := inttostr(FCursorcount);
  333. inc(FCursorCount);
  334. // Prior to v8 there is no support for cursors and parameters.
  335. // So that's not supported.
  336. if FStatementType = stselect then
  337. statement := 'DECLARE slctst' + name + nr +' BINARY CURSOR FOR ' + buf
  338. else if FStatementType in [stInsert,stUpdate,stDelete] then
  339. begin
  340. tr := aTransaction.Handle;
  341. // Only available for pq 8.0, so don't use it...
  342. // Res := pqprepare(tr,'prepst'+name+nr,pchar(buf),params.Count,pchar(''));
  343. s := 'prepare prepst'+nr+' ';
  344. if Assigned(AParams) and (AParams.count > 0) then
  345. begin
  346. s := s + '(';
  347. for i := 0 to AParams.count-1 do
  348. begin
  349. s := s + TypeStrings[AParams[i].DataType] + ',';
  350. buf := stringreplace(buf,':'+AParams[i].Name,'$'+inttostr(i+1),[rfReplaceAll,rfIgnoreCase]);
  351. end;
  352. s[length(s)] := ')';
  353. end;
  354. s := s + ' as ' + buf;
  355. res := pqexec(tr,pchar(s));
  356. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  357. begin
  358. pqclear(res);
  359. DatabaseError(SErrPrepareFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
  360. end;
  361. FPrepared := True;
  362. end
  363. else
  364. statement := buf;
  365. end;
  366. end;
  367. procedure TPQConnection.CloseStatement(cursor : TSQLCursor);
  368. begin
  369. with cursor as TPQCursor do
  370. if (PQresultStatus(res) <> PGRES_FATAL_ERROR) then //Don't try to do anything if the transaction has already encountered an error.
  371. begin
  372. if FStatementType = stselect then
  373. begin
  374. Res := pqexec(tr,pchar('CLOSE slctst' + name + nr));
  375. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  376. begin
  377. pqclear(res);
  378. DatabaseError(SErrClearSelection + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
  379. end
  380. end;
  381. pqclear(baseres);
  382. pqclear(res);
  383. end;
  384. end;
  385. procedure TPQConnection.UnPrepareStatement(cursor : TSQLCursor);
  386. begin
  387. with (cursor as TPQCursor) do if FPrepared then
  388. begin
  389. res := pqexec(tr,pchar('deallocate prepst'+nr));
  390. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  391. begin
  392. pqclear(res);
  393. DatabaseError(SErrPrepareFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
  394. end;
  395. pqclear(res);
  396. FPrepared := False;
  397. end;
  398. end;
  399. procedure TPQConnection.FreeFldBuffers(cursor : TSQLCursor);
  400. begin
  401. // Do nothing
  402. end;
  403. procedure TPQConnection.Execute(cursor: TSQLCursor;atransaction:tSQLtransaction;AParams : TParams);
  404. var ar : array of pchar;
  405. i : integer;
  406. s : string;
  407. begin
  408. with cursor as TPQCursor do
  409. begin
  410. if FStatementType in [stInsert,stUpdate,stDelete] then
  411. begin
  412. if Assigned(AParams) and (AParams.count > 0) then
  413. begin
  414. setlength(ar,Aparams.count);
  415. for i := 0 to AParams.count -1 do
  416. ar[i] := pchar(AParams[i].asstring);
  417. res := PQexecPrepared(tr,pchar('prepst'+nr),Aparams.count,@Ar[0],nil,nil,0)
  418. end
  419. else
  420. res := PQexecPrepared(tr,pchar('prepst'+nr),0,nil,nil,nil,0);
  421. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  422. begin
  423. pqclear(res);
  424. DatabaseError(SErrExecuteFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self);
  425. end;
  426. end
  427. else
  428. begin
  429. tr := aTransaction.Handle;
  430. s := statement;
  431. //Should be altered, just like in TSQLQuery.ApplyRecUpdate
  432. if assigned(AParams) then for i := 0 to AParams.count-1 do
  433. s := stringreplace(s,':'+AParams[i].Name,AParams[i].asstring,[rfReplaceAll,rfIgnoreCase]);
  434. res := pqexec(tr,pchar(s));
  435. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  436. begin
  437. pqclear(res);
  438. DatabaseError(SErrExecuteFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self);
  439. end;
  440. end;
  441. end;
  442. end;
  443. procedure TPQConnection.AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs);
  444. var
  445. i : integer;
  446. size : integer;
  447. st : string;
  448. fieldtype : tfieldtype;
  449. begin
  450. with cursor as TPQCursor do
  451. begin
  452. // BaseRes := pqexecParams(tr,'FETCH 0 IN selectst' + pchar(name) ,0,nil,nil,nil,nil,1);
  453. st := pchar('FETCH 0 IN slctst' + name+nr);
  454. BaseRes := pqexec(tr,pchar(st));
  455. if (PQresultStatus(BaseRes) <> PGRES_TUPLES_OK) then
  456. begin
  457. pqclear(BaseRes);
  458. DatabaseError(SErrFieldDefsFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
  459. end;
  460. nFields := PQnfields(BaseRes);
  461. for i := 0 to nFields-1 do
  462. begin
  463. size := PQfsize(BaseRes, i);
  464. fieldtype := TranslateFldType(PQftype(BaseRes, i));
  465. if (fieldtype = ftstring) and (size = -1) then
  466. size := pqfmod(baseres,i)-3;
  467. if fieldtype = ftdate then
  468. size := sizeof(double);
  469. TFieldDef.Create(FieldDefs, PQfname(BaseRes, i), fieldtype,size, False, (i + 1));
  470. end;
  471. end;
  472. end;
  473. function TPQConnection.GetHandle: pointer;
  474. begin
  475. Result := FSQLDatabaseHandle;
  476. end;
  477. function TPQConnection.Fetch(cursor : TSQLCursor) : boolean;
  478. var st : string;
  479. begin
  480. with cursor as TPQCursor do
  481. begin
  482. st := pchar('FETCH NEXT IN slctst' + name+nr);
  483. Res := pqexec(tr,pchar(st));
  484. if (PQresultStatus(res) <> PGRES_TUPLES_OK) then
  485. begin
  486. pqclear(Res);
  487. DatabaseError(SErrfetchFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
  488. end;
  489. Result := (PQntuples(res)<>0);
  490. end;
  491. end;
  492. function TPQConnection.LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer) : boolean;
  493. var
  494. x,i : integer;
  495. li : Longint;
  496. CurrBuff : pchar;
  497. tel : byte;
  498. dbl : pdouble;
  499. begin
  500. with cursor as TPQCursor do
  501. begin
  502. for x := 0 to PQnfields(res)-1 do
  503. if PQfname(Res, x) = FieldDef.Name then break;
  504. if PQfname(Res, x) <> FieldDef.Name then
  505. DatabaseErrorFmt(SFieldNotFound,[FieldDef.Name],self);
  506. if pqgetisnull(res,0,x)=1 then
  507. result := false
  508. else
  509. begin
  510. i := PQfsize(res, x);
  511. CurrBuff := pqgetvalue(res,0,x);
  512. case FieldDef.DataType of
  513. ftInteger, ftSmallint, ftLargeInt,ftfloat :
  514. begin
  515. for tel := 1 to i do // postgres returns big-endian numbers
  516. pchar(Buffer)[tel-1] := CurrBuff[i-tel];
  517. end;
  518. ftString :
  519. begin
  520. li := pqgetlength(res,0,x);
  521. Move(CurrBuff^, Buffer^, li);
  522. pchar(Buffer + li)^ := #0;
  523. i := pqfmod(res,x)-3;
  524. end;
  525. ftdate :
  526. begin
  527. li := 0;
  528. for tel := 1 to i do // postgres returns big-endian numbers
  529. pchar(@li)[tel-1] := CurrBuff[i-tel];
  530. // double(buffer^) := x + 36526; This doesn't work, please tell me what is wrong with it?
  531. dbl := pointer(buffer);
  532. dbl^ := li + 36526;
  533. i := sizeof(double);
  534. end;
  535. ftDateTime, fttime :
  536. begin
  537. dbl := pointer(buffer);
  538. dbl^ := 0;
  539. for tel := 1 to i do // postgres returns big-endian numbers
  540. pchar(Buffer)[tel-1] := CurrBuff[i-tel];
  541. dbl^ := (dbl^+3.1558464E+009)/86400; // postgres counts seconds elapsed since 1-1-2000
  542. end;
  543. ftBCD:
  544. begin
  545. // not implemented
  546. end;
  547. ftBoolean:
  548. pchar(buffer)[0] := CurrBuff[0]
  549. end;
  550. result := true;
  551. end;
  552. end;
  553. end;
  554. procedure TPQConnection.UpdateIndexDefs(var IndexDefs : TIndexDefs;TableName : string);
  555. var qry : TSQLQuery;
  556. begin
  557. if not assigned(Transaction) then
  558. DatabaseError(SErrConnTransactionnSet);
  559. qry := tsqlquery.Create(nil);
  560. qry.transaction := Transaction;
  561. qry.database := Self;
  562. with qry do
  563. begin
  564. ReadOnly := True;
  565. sql.clear;
  566. sql.add('select '+
  567. 'ic.relname as indexname, '+
  568. 'tc.relname as tablename, '+
  569. 'ia.attname, '+
  570. 'i.indisprimary, '+
  571. 'i.indisunique '+
  572. 'from '+
  573. 'pg_attribute ta, '+
  574. 'pg_attribute ia, '+
  575. 'pg_class tc, '+
  576. 'pg_class ic, '+
  577. 'pg_index i '+
  578. 'where '+
  579. '(i.indrelid = tc.oid) and '+
  580. '(ta.attrelid = tc.oid) and '+
  581. '(ia.attrelid = i.indexrelid) and '+
  582. '(ic.oid = i.indexrelid) and '+
  583. '(ta.attnum = i.indkey[ia.attnum-1]) and '+
  584. '(upper(tc.relname)=''' + UpperCase(TableName) +''') '+
  585. 'order by '+
  586. 'ic.relname;');
  587. open;
  588. end;
  589. while not qry.eof do with IndexDefs.AddIndexDef do
  590. begin
  591. Name := trim(qry.fields[0].asstring);
  592. Fields := trim(qry.Fields[2].asstring);
  593. If qry.fields[3].asboolean then options := options + [ixPrimary];
  594. If qry.fields[4].asboolean then options := options + [ixUnique];
  595. qry.next;
  596. while (name = qry.fields[0].asstring) and (not qry.eof) do
  597. begin
  598. Fields := Fields + ';' + trim(qry.Fields[2].asstring);
  599. qry.next;
  600. end;
  601. end;
  602. qry.close;
  603. qry.free;
  604. end;
  605. end.