1------------------------------------------------------------------------------ 2-- -- 3-- APQ DATABASE BINDINGS -- 4-- -- 5-- A P Q - POSTGRESQL -- 6-- -- 7-- B o d y -- 8-- -- 9-- Copyright (C) 2002-2007, Warren W. Gay VE3WWG -- 10-- Copyright (C) 2007-2011, KOW Framework Project -- 11-- -- 12-- -- 13-- APQ is free software; you can redistribute it and/or modify it under -- 14-- terms of the GNU General Public License as published by the Free Soft- -- 15-- ware Foundation; either version 2, or (at your option) any later ver- -- 16-- sion. APQ is distributed in the hope that it will be useful, but WITH- -- 17-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- 18-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -- 19-- for more details. You should have received a copy of the GNU General -- 20-- Public License distributed with APQ; see file COPYING. If not, write -- 21-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- 22-- MA 02111-1307, USA. -- 23-- -- 24-- As a special exception, if other files instantiate generics from this -- 25-- unit, or you link this unit with other files to produce an executable, -- 26-- this unit does not by itself cause the resulting executable to be -- 27-- covered by the GNU General Public License. This exception does not -- 28-- however invalidate any other reasons why the executable file might be -- 29-- covered by the GNU Public License. -- 30------------------------------------------------------------------------------ 31 32with Ada.Exceptions; 33with Ada.Calendar; 34with Ada.Unchecked_Deallocation; 35with Ada.Unchecked_Conversion; 36with Ada.Characters.Latin_1; 37with Ada.Characters.Handling; 38with Ada.Strings.Fixed; 39with ada.strings.maps; 40with Ada.IO_Exceptions; 41with System; 42with System.Address_To_Access_Conversions; 43with Interfaces.C.Strings; 44with GNAT.OS_Lib; 45 46use Interfaces.C; 47use Ada.Exceptions; 48 49package body APQ.PostgreSQL.Client is 50 51 Seek_Set : constant Interfaces.C.int := 0; 52 Seek_Cur : constant Interfaces.C.int := 1; 53 Seek_End : constant Interfaces.C.int := 2; 54 No_Date : Ada.Calendar.Time; 55 56 type PQ_Status_Type is ( 57 Connection_OK, 58 Connection_Bad, 59 Connection_Started, -- Waiting for connection to be made. 60 Connection_Made, -- Connection OK; waiting to send. 61 Connection_Awaiting_Response, -- Waiting for a response 62 Connection_Auth_OK, -- Received authentication 63 Connection_Setenv, -- Negotiating environment. 64 Connection_ssl_startup, 65 Connection_needed 66 ); 67 68 for PQ_Status_Type use ( 69 0, -- CONNECTION_OK 70 1, -- CONNECTION_BAD 71 2, -- CONNECTION_STARTED 72 3, -- CONNECTION_MADE 73 4, -- CONNECTION_AWAITING_RESPONSE 74 5, -- CONNECTION_AUTH_OK 75 6, -- CONNECTION_SETENV 76 7, -- Connection_ssl_startup 77 8 -- Connection_needed 78 79 ); 80 pragma convention(C,PQ_Status_Type); 81 82 83 ------------------------------ 84 -- DATABASE CONNECTION : 85 ------------------------------ 86 87 88 function Engine_Of(C : Connection_Type) return Database_Type is 89 begin 90 return Engine_PostgreSQL; 91 end Engine_Of; 92 93 94 95 function New_Query(C : Connection_Type) return Root_Query_Type'Class is 96 Q : Query_Type; 97 begin 98 return Q; 99 end New_Query; 100 101 102 103 procedure Notify_on_Standard_Error(C : in out Connection_Type; Message : String) is 104 use Ada.Text_IO; 105 begin 106 Put(Standard_Error,"*** NOTICE : "); 107 Put_Line(Standard_Error,Message); 108 end Notify_on_Standard_Error; 109 110 111 112 procedure Set_Instance(C : in out Connection_Type; Instance : String) is 113 begin 114 Raise_Exception(Not_Supported'Identity, 115 "PG01: PostgreSQL has no Instance ID. (Set_Instance)"); 116 end Set_Instance; 117 118 119 120 function Host_Name(C : Connection_Type) return String is 121 begin 122 if not Is_Connected(C) then 123 return Host_Name(Root_Connection_Type(C)); 124 else 125 declare 126 use Interfaces.C.Strings; 127 function PQhost(PGconn : PG_Conn) return chars_ptr; 128 pragma Import(C,PQhost,"PQhost"); 129 130 The_Host : chars_ptr := PQhost(C.Connection); 131 begin 132 if The_Host = Null_Ptr then 133 return "localhost"; 134 end if; 135 return Value_Of(The_Host); 136 end; 137 end if; 138 end Host_Name; 139 140 141 142 function Port(C : Connection_Type) return Integer is 143 begin 144 if not Is_Connected(C) then 145 return Port(Root_Connection_Type(C)); 146 else 147 declare 148 use Interfaces.C.Strings; 149 function PQport(PGconn : PG_Conn) return chars_ptr; 150 pragma Import(C,PQport,"PQport"); 151 152 The_Port : String := Value_Of(PQport(C.Connection)); 153 begin 154 return Integer'Value(The_Port); 155 exception 156 when others => 157 Raise_Exception(Invalid_Format'Identity, 158 "PG02: Invalid port number or is a UNIX socket reference (Port)."); 159 end; 160 end if; 161 162 return 0; 163 end Port; 164 165 166 167 function Port(C : Connection_Type) return String is 168 begin 169 if not Is_Connected(C) then 170 return Port(Root_Connection_Type(C)); 171 else 172 declare 173 use Interfaces.C.Strings; 174 function PQport(PGconn : PG_Conn) return chars_ptr; 175 pragma Import(C,PQport,"PQport"); 176 begin 177 return Value_Of(PQport(C.Connection)); 178 end; 179 end if; 180 181 end Port; 182 183 184 185 function DB_Name(C : Connection_Type) return String is 186 begin 187 if not Is_Connected(C) then 188 return To_Case(DB_Name(Root_Connection_Type(C)),C.SQL_Case); 189 else 190 declare 191 use Interfaces.C.Strings; 192 function PQdb(PGconn : PG_Conn) return chars_ptr; 193 pragma Import(C,PQdb,"PQdb"); 194 begin 195 return Value_Of(PQdb(C.Connection)); 196 end; 197 end if; 198 199 end DB_Name; 200 201 202 203 function User(C : Connection_Type) return String is 204 begin 205 if not Is_Connected(C) then 206 return User(Root_Connection_Type(C)); 207 else 208 declare 209 use Interfaces.C.Strings; 210 function PQuser(PGconn : PG_Conn) return chars_ptr; 211 pragma Import(C,PQuser,"PQuser"); 212 begin 213 return Value_Of(PQuser(C.Connection)); 214 end; 215 end if; 216 end User; 217 218 219 220 function Password(C : Connection_Type) return String is 221 begin 222 if not Is_Connected(C) then 223 return Password(Root_Connection_Type(C)); 224 else 225 declare 226 use Interfaces.C.Strings; 227 function PQpass(PGconn : PG_Conn) return chars_ptr; 228 pragma Import(C,PQpass,"PQpass"); 229 begin 230 return Value_Of(PQpass(C.Connection)); 231 end; 232 end if; 233 end Password; 234 235 236 237 procedure Set_DB_Name(C : in out Connection_Type; DB_Name : String) is 238 239 procedure Use_Database(C : in out Connection_Type; DB_Name : String) is 240 Q : Query_Type; 241 begin 242 begin 243 Prepare(Q,To_Case("USE " & DB_Name,C.SQL_Case)); 244 Execute(Q,C); 245 exception 246 when SQL_Error => 247 Raise_Exception(APQ.Use_Error'Identity, 248 "PG03: Unable to select database " & DB_Name & ". (Use_Database)"); 249 end; 250 end Use_Database; 251 252 begin 253 if not Is_Connected(C) then 254 -- Modify context to connect to this database when we connect 255 Set_DB_Name(Root_Connection_Type(C),DB_Name); 256 else 257 -- Use this database now 258 Use_Database(C,DB_Name); 259 -- Update context info if no exception thrown above 260 Set_DB_Name(Root_Connection_Type(C),DB_Name); 261 end if; 262 263 C.keyname_val_cache_uptodate := false; 264 265 end Set_DB_Name; 266 267 268 269 procedure Set_Options(C : in out Connection_Type; Options : String) is 270 begin 271 Replace_String(C.Options,Set_Options.Options); 272 C.keyname_val_cache_uptodate := false; 273 end Set_Options; 274 275 276 277 function Options(C : Connection_Type) return String is 278 begin 279 if not Is_Connected(C) then 280 if C.Options /= null then 281 return C.Options.all; 282 end if; 283 else 284 declare 285 use Interfaces.C.Strings; 286 function PQoptions(PGconn : PG_Conn) return chars_ptr; 287 pragma Import(C,PQoptions,"PQoptions"); 288 begin 289 return Value_Of(PQoptions(C.Connection)); 290 end; 291 end if; 292 return ""; 293 end Options; 294 295 296 297 procedure Set_Notify_Proc(C : in out Connection_Type; Notify_Proc : Notify_Proc_Type) is 298 begin 299 C.Notify_Proc := Set_Notify_Proc.Notify_Proc; 300 end Set_Notify_Proc; 301 302 303 304 function Notify_Proc(C : Connection_Type) return Notify_Proc_Type is 305 begin 306 return C.Notify_Proc; 307 end Notify_Proc; 308 309 310 -------------------------------------------------- 311 -- Connection_Notify is called by notices.c as 312 -- a callback from the libpq interface. 313 -------------------------------------------------- 314-- procedure Connection_Notify(C_Addr : System.Address; Msg_Ptr : Interfaces.C.Strings.chars_ptr); 315-- pragma Export(C,Connection_Notify,"Connection_Notify"); 316 317 318 procedure Connection_Notify(C_Addr : System.Address; Msg_Ptr : Interfaces.C.Strings.chars_ptr) is 319 use Interfaces.C.Strings; 320 package Addr is new System.Address_To_Access_Conversions(Connection_Type); 321 322 function Strip_Prefix(S : String) return String is 323 use Ada.Strings.Fixed, Ada.Strings; 324 begin 325 if S(S'First..S'First+6) = "NOTICE:" then 326 return Trim(S(S'First+7..S'Last),Left); 327 end if; 328 return S; 329 end Strip_Prefix; 330 331 Abrt_Notice : constant String := "current transaction is aborted, queries ignored until end of transaction block"; 332 Conn : Addr.Object_Pointer := Addr.To_Pointer(C_Addr); 333 Msg : String := Strip_Prefix(Strip_NL(To_Ada_String(Msg_Ptr))); 334 begin 335 if Conn.Notice /= null then 336 Free(Conn.Notice); -- Free last notice 337 end if; 338 -- Store new notice 339 Conn.Notice := new String(1..Msg'Length); 340 Conn.Notice.all := Msg; 341 342 if Conn.Notice.all = Abrt_Notice then 343 Conn.Abort_State := True; 344 end if; 345 346 if Conn.Notify_Proc /= Null then 347 Conn.Notify_Proc(Conn.all,Conn.Notice.all); 348 end if; 349 350 end Connection_Notify; 351 352 353 354 function PQ_Status(C : Connection_Type) return PQ_Status_Type is 355 function PQstatus(C : PG_Conn) return PQ_Status_Type; 356 pragma Import(C,PQstatus,"PQstatus"); 357 begin 358 if C.Connection = Null_Connection then 359 return Connection_Bad; 360 else 361 return PQstatus(C.Connection); 362 end if; 363 end PQ_Status; 364 365 procedure Disconnect(C : in out Connection_Type) is 366 procedure Notice_Uninstall(C : PG_Conn); 367 pragma Import(C,notice_uninstall,"notice_uninstall"); 368 procedure PQfinish(C : PG_Conn); 369 pragma Import(C,PQfinish,"PQfinish"); 370 begin 371 372 if not Is_Connected(C) then 373 Raise_Exception(Not_Connected'Identity, 374 "PG09: Not connected. (Disconnect)"); 375 end if; 376 377 Notice_Uninstall(C.Connection); -- Disconnect callback notices 378 PQfinish(C.Connection); -- Now release the connection 379 C.Connection := Null_Connection; 380 C.Abort_State := False; -- Clear abort state 381 C.Notify_Proc := null; -- De-register the notify procedure 382 383 if C.Trace_Mode = Trace_APQ or else C.Trace_Mode = Trace_Full then 384 Ada.Text_IO.Put_Line(C.Trace_Ada,"-- DISCONNECT"); 385 end if; 386 387 Reset(C); 388 389 end Disconnect; 390 391 392 393 function Is_Connected(C : Connection_Type) return Boolean is 394 begin 395 return PQ_Status(C) = Connection_OK; 396 end Is_Connected; 397 398 399 400 procedure Internal_Reset(C : in out Connection_Type; In_Finalize : Boolean := False) is 401 begin 402 Free_Ptr(C.Error_Message); 403 404 if C.Connection /= Null_Connection then 405 declare 406 Q : Query_Type; 407 begin 408 Clear_Abort_State(C); 409 if C.Rollback_Finalize or In_Abort_State(C) then 410 if C.Trace_On and then C.Trace_Filename /= null and then In_Finalize = True then 411 Ada.Text_IO.Put_Line(C.Trace_Ada,"-- ROLLBACK ON FINALIZE"); 412 end if; 413 Rollback_Work(Q,C); 414 else 415 if C.Trace_On and then C.Trace_Filename /= null and then In_Finalize = True then 416 Ada.Text_IO.Put_Line(C.Trace_Ada,"-- COMMIT ON FINALIZE"); 417 end if; 418 Commit_Work(Q,C); 419 end if; 420 exception 421 when others => 422 null; -- Ignore if the Rollback/commit fails 423 end; 424 425 Clear_Abort_State(C); 426 427 Disconnect(C); 428 429 if C.Trace_Filename /= null then 430 Close_DB_Trace(C); 431 end if; 432 433 end if; 434 435 if C.Connection = Null_Connection then 436 Free_Ptr(C.Host_Name); 437 Free_Ptr(C.Host_Address); 438 Free_Ptr(C.DB_Name); 439 Free_Ptr(C.User_Name); 440 Free_Ptr(C.User_Password); 441 Free_Ptr(C.Options); 442 Free_Ptr(C.Error_Message); 443 Free_Ptr(C.Notice); 444 -- 445 clear_all_key_nameval(c); 446 447 end if; 448 end Internal_Reset; 449 450 451 452 procedure Reset(C : in out Connection_Type) is 453 begin 454 Internal_Reset(C,In_Finalize => False); 455 end Reset; 456 457 458 459 function Error_Message(C : Connection_Type) return String is 460 function PQerrorMessage(C : PG_Conn) return Interfaces.C.Strings.chars_ptr; 461 pragma Import(C,PQerrorMessage,"PQerrorMessage"); 462 begin 463 if C.Connection = Null_Connection then 464 if C.Error_Message /= null then 465 return C.Error_Message.all; 466 else 467 return ""; 468 end if; 469 else 470 return To_Ada_String(PQerrorMessage(C.Connection)); 471 end if; 472 end Error_Message; 473 474 475 476 function Notice_Message(C : Connection_Type) return String is 477 begin 478 if C.Notice /= null then 479 return C.Notice.all; 480 end if; 481 return ""; 482 end Notice_Message; 483 -- 484 -- 485 function "="( Left :root_option_record2; right : root_option_record2) return boolean 486 is 487 pragma Optimize(time); 488 489 lkey_s : string := 490 ada.Strings.fixed.Trim( ada.Characters.Handling.To_Lower( 491 ada.Strings.Unbounded.To_String( left.key_u)) , 492 ada.Strings.Both ); 493 rkey_s : string := 494 ada.Strings.fixed.Trim( ada.Characters.Handling.To_Lower( 495 ada.Strings.Unbounded.To_String( right.key_u)) , 496 ada.Strings.Both ); 497 begin 498 if lkey_s = rkey_s then 499 return true; 500 end if; 501 return false; 502 end "="; 503 504 function quote_string( qkv : string ) return String 505 is 506 use ada.Strings; 507 use ada.Strings.Fixed; 508 509 function PQescapeString(to, from : System.Address; length : size_t) return size_t; 510 pragma Import(C,PQescapeString,"PQescapeString"); 511 src : string := trim ( qkv , both ); 512 C_Length : size_t := src'Length * 2 + 1; 513 C_From : char_array := To_C(src); 514 C_To : char_array(0..C_Length-1); 515 R_Length : size_t := PQescapeString(C_To'Address,C_From'Address,C_Length); 516 -- viva!!! :-) 517 begin 518 return To_Ada(C_To); 519 end quote_string; 520 ---- 521 522 function quote_string( qkv : string ) return ada.Strings.Unbounded.Unbounded_String 523 is 524 begin 525 return ada.Strings.Unbounded.To_Unbounded_String(String'(quote_string(qkv))); 526 end quote_string; 527 -- 528 function cache_key_nameval_uptodate( C : Connection_Type) -- 529 return boolean 530 is 531 begin 532 return c.keyname_val_cache_uptodate; 533 end cache_key_nameval_uptodate; 534 535 -- 536 procedure cache_key_nameval_create( C : in out Connection_Type; force : boolean := false)-- 537 is 538 pragma optimize(time); 539 use ada.strings.Unbounded; 540 use ada.strings.Fixed; 541 use ada.Strings; 542 use Ada.Characters.Handling; 543 544 use apq.postgresql.client.options_list2; 545 -- 546 tmp_ub_cache : Unbounded_String := To_Unbounded_String(160); -- pre-allocate :-) 547 tmp_eq : Unbounded_String := to_Unbounded_String(" = '"); 548 tmp_ap : Unbounded_String := to_Unbounded_String("' "); 549 -- 550 procedure process(position : cursor) is 551 val_tmp : root_option_record2 := element(position); 552 begin 553 if val_tmp.is_valid = false then return; end if; --bahiii! :-) 554 555 tmp_ub_cache := tmp_ub_cache & val_tmp.key_u & tmp_eq & 556 trim(Unbounded_String'(quote_string(string'(To_String(val_tmp.value_u)))),ada.Strings.Both) 557 & tmp_ap ; 558 559 end process; 560 561 begin 562 if cache_key_nameval_uptodate( C ) and force = false then return; end if; -- bahiii :-) 563 c.keyname_val_cache := To_Unbounded_String(""); 564 565 if c.Port_Format = UNIX_Port then 566 tmp_ub_cache := to_Unbounded_String("host") 567 & tmp_eq & trim(Unbounded_String'(quote_string(string'(To_String(C.Host_Name)))),ada.Strings.both) & tmp_ap 568 & to_Unbounded_String("port") 569 & tmp_eq & trim(Unbounded_String'(quote_string(string'(To_String(C.Port_Name)))),ada.Strings.both) & tmp_ap ; 570 elsif c.Port_Format = IP_Port then 571 tmp_ub_cache := to_Unbounded_String("hostaddr") 572 & tmp_eq & trim(Unbounded_String'(quote_string(string'(To_String(C.Host_Address)))),ada.Strings.both) & tmp_ap 573 & to_Unbounded_String("port") 574 & tmp_eq & trim(to_Unbounded_String(string'(Port_Integer'image(c.Port_Number))),ada.Strings.both) & tmp_ap; 575 else 576 raise program_error; 577 end if; 578 579 tmp_ub_cache := tmp_ub_cache 580 & to_Unbounded_String("dbname") & tmp_eq & trim(Unbounded_String'(quote_string(string'(To_String(C.DB_Name)))),ada.Strings.both) & tmp_ap 581 & to_Unbounded_String("user") & tmp_eq & trim(Unbounded_String'(quote_string(string'(To_String(C.User_Name)))),ada.Strings.both) & tmp_ap 582 & to_Unbounded_String("password") & tmp_eq & trim(Unbounded_String'(quote_string(string'(To_String(C.User_Password)))),ada.Strings.both) & tmp_ap; 583 if trim(string'(To_String(C.Options)), ada.Strings.Both) /= "" then 584 tmp_ub_cache := tmp_ub_cache 585 & to_Unbounded_String("options") & tmp_eq & trim(Unbounded_String'(quote_string(string'(To_String(C.Options)))), both) & tmp_ap ; 586 end if; 587 588 if not (c.key_name_list.Is_Empty ) then 589 c.key_name_list.Iterate(process'Access); 590 end if; 591 592 c.keyname_val_cache := tmp_ub_cache; 593 594 tmp_ub_cache := To_Unbounded_String(""); 595 596 end cache_key_nameval_create;-- 597 -- 598 procedure clear_all_key_nameval(C : in out Connection_Type ) 599 is 600 pragma optimize(time); 601 begin 602 if not ( c.key_name_list.is_empty ) then 603 c.key_name_list.clear; 604 end if; 605 c.keyname_val_cache := ada.Strings.Unbounded.To_Unbounded_String(""); 606 c.keyname_val_cache_uptodate := false; 607 608 end clear_all_key_nameval; 609 610 procedure key_nameval( L : in out options_list2.list ; 611 val : root_option_record2; 612 clear : boolean := false 613 ) 614 is 615 use options_list2; 616 mi_cursor : options_list2.cursor := no_element; 617 begin 618 if clear then 619 if not ( L.is_empty ) then 620 L.clear; 621 end if; 622 end if; 623 if L.is_empty then 624 L.append(val); 625 return; 626 end if; 627 mi_cursor := L.find(val); 628 if mi_cursor = No_Element then 629 L.append(val); 630 return; 631 end if; 632 L.replace_element(mi_cursor, val); 633 634 end key_nameval; 635 636 637 procedure add_key_nameval( C : in out Connection_Type; 638 kname, kval : string := ""; 639 clear : boolean := false ) 640 is 641 pragma optimize(time); 642 use ada.strings; 643 use ada.Strings.Fixed; 644 645 tmp_kname : string := string'(trim(kname,both)); 646 tmp_kval : string := string'(trim(kval,both)); 647 648 begin 649 if tmp_kname = "" then return; end if; -- bahiii :-) 650 declare 651 val_tmp : root_option_record2 := 652 root_option_record2'(is_valid => true, 653 key_u => ada.Strings.Unbounded.To_Unbounded_String(tmp_kname), 654 value_u => ada.Strings.Unbounded.To_Unbounded_String(tmp_kval) 655 ); 656 begin 657 key_nameval(L => c.key_name_list, 658 val => val_tmp , 659 clear => clear); 660 end; 661 C.keyname_val_cache_uptodate := false; 662 663 end add_key_nameval; 664 665 -- 666 procedure clone_clone_pg(To : in out Connection_Type; From : Connection_Type ) 667 is 668 pragma optimize(time); 669 use apq.postgresql.client.options_list2; 670 -- 671 procedure add(position : cursor) is 672 begin 673 to.key_name_list.append(element(position)); 674 end add; 675 676 begin 677 clear_all_key_nameval(to); 678 679 if not ( from.key_name_list.is_empty ) then 680 from.key_name_list.iterate(add'Access); 681 end if; 682 683 to.keyname_val_cache_uptodate := false; 684 685 end clone_clone_pg; 686 687 -- 688 procedure connect(C : in out Connection_Type; Check_Connection : Boolean := True) 689 is 690 pragma optimize(time); 691 692 use Interfaces.C.Strings; 693 694 begin 695 if Check_Connection and then Is_Connected(C) then 696 Raise_Exception(Already_Connected'Identity, 697 "PG07: Already connected (Connect)."); 698 end if; 699 700 cache_key_nameval_create(C); -- don't worry :-) "re-create" accours only if not uptodate :-) 701 -- This procedure can be executed manually if you desire :-) 702 -- "for example": the "Connection_type" var was created and configured 703 -- much before the connection with the DataBase server :-) take place 704 -- then the "Connection_type" already uptodate 705 -- ( well... uptodate if really uptodate ;-) 706 -- this will speedy up the things a little :-) 707 declare 708 procedure Notice_Install(Conn : PG_Conn; ada_obj_ptr : System.Address); 709 pragma import(C,Notice_Install,"notice_install"); 710 711 function PQconnectdb(coni : chars_ptr ) return PG_Conn; 712 pragma import(C,PQconnectdb,"PQconnectdb"); 713 coni_str : string := ada.Strings.Unbounded.To_String(C.keyname_val_cache); 714 C_conni : chars_ptr := New_String(Str => coni_str ); 715 begin 716 C.Connection := PQconnectdb( C_conni); -- blocking call :-) 717 Free_Ptr(C.Error_Message); 718 719 if PQ_Status(C) /= Connection_OK then -- if the connecting in a non-blocking fashion, 720 -- there are more option of status needing verification :-) 721 -- it Don't the case here 722 declare 723 procedure PQfinish(C : PG_Conn); 724 pragma Import(C,PQfinish,"PQfinish"); 725 Msg : String := Strip_NL(Error_Message(C)); 726 begin 727 PQfinish(C.Connection); 728 C.Connection := Null_Connection; 729 C.Error_Message := new String(1..Msg'Length); 730 C.Error_Message.all := Msg; 731 Raise_Exception(Not_Connected'Identity, 732 "PG08: Failed to connect to database server (Connect). error was: " & 733 msg ); -- more descriptive about 'what failed' :-) 734 end; 735 end if; 736 737 Notice_Install(C.Connection,C'Address); -- Install Connection_Notify handler 738 739 ------------------------------ 740 -- SET PGDATESTYLE TO ISO; 741 -- 742 -- This is necessary for all of the 743 -- APQ date handling routines to 744 -- function correctly. This implies 745 -- that all APQ applications programs 746 -- should use the ISO date format. 747 ------------------------------ 748 declare 749 SQL : Query_Type; 750 begin 751 Prepare(SQL,"SET DATESTYLE TO ISO"); 752 Execute(SQL,C); 753 exception 754 when Ex : others => 755 Disconnect(C); 756 Reraise_Occurrence(Ex); 757 end; 758 end; 759 760 end connect; 761 762 procedure connect(C : in out Connection_Type; Same_As : Root_Connection_Type'Class) 763 is 764 pragma optimize(time); 765 766 type Info_Func is access function(C : Connection_Type) return String; 767 768 procedure Clone(S : in out String_Ptr; Get_Info : Info_Func) is 769 Info : String := Get_Info(Connection_Type(Same_As)); 770 begin 771 if Info'Length > 0 then 772 S := new String(1..Info'Length); 773 S.all := Info; 774 else 775 null; 776 pragma assert(S = null); 777 end if; 778 end Clone; 779 blo : boolean := true; 780 tmpex : natural := 2; 781 begin 782 Reset(C); 783 784 Clone(C.Host_Name,Host_Name'Access); 785 786 C.Port_Format := Same_As.Port_Format; 787 if C.Port_Format = IP_Port then 788 C.Port_Number := Port(Same_As); -- IP_Port 789 else 790 Clone(C.Port_Name,Port'Access); -- UNIX_Port 791 end if; 792 793 Clone(C.DB_Name,DB_Name'Access); 794 Clone(C.User_Name,User'Access); 795 Clone(C.User_Password,Password'Access); 796 Clone(C.Options,Options'Access); 797 798 C.Rollback_Finalize := Same_As.Rollback_Finalize; 799 C.Notify_Proc := Connection_Type(Same_As).Notify_Proc; 800 -- I believe if "Same_As" var is defacto a "Connection_Type" as "C" var, 801 -- there are need for copy key's name and val from "Same_As" , 802 -- because in this keys and vals 803 -- maybe are key's how sslmode , gsspi etc, that are defacto needs for connecting "C" 804 805 if Same_As.Engine_Of = Engine_PostgreSQL then 806 clone_clone_pg(C , Connection_Type(Same_as)); 807 end if; 808 809 connect(C); -- Connect to database before worrying about trace facilities 810 811 -- TRACE FILE & TRACE SETTINGS ARE NOT CLONED 812 813 end connect; 814 815 function verifica_conninfo_cache( C : Connection_Type) return string -- for debug purpose :-P 816 -- in the spirit there are an get_password(c) yet... 817 818 is 819 begin 820 return ada.Strings.Unbounded.To_String(c.keyname_val_cache); 821 end verifica_conninfo_cache; 822 823 824 825 826 procedure Open_DB_Trace(C : in out Connection_Type; Filename : String; Mode : Trace_Mode_Type := Trace_APQ) is 827 begin 828 if C.Trace_Filename /= null then 829 Raise_Exception(Tracing_State'Identity, 830 "PG04: Already in a tracing state (Open_DB_Trace)."); 831 end if; 832 833 if not Is_Connected(C) then 834 Raise_Exception(Not_Connected'Identity, 835 "PG05: Not connected (Open_DB_Trace)."); 836 end if; 837 838 if Mode = Trace_None then 839 pragma assert(C.Trace_Mode = Trace_None); 840 return; -- No trace required 841 end if; 842 843 declare 844 use CStr, System, Ada.Text_IO, Ada.Text_IO.C_Streams; 845 procedure PQtrace(PGconn : PG_Conn; debug_port : CStr.FILEs); 846 pragma Import(C,PQtrace,"PQtrace"); 847 848 C_Filename : char_array := To_C(Filename); 849 File_Mode : char_array := To_C("a"); 850 begin 851 C.Trace_File := fopen(C_Filename'Address,File_Mode'Address); 852 if C.Trace_File = Null_Stream then 853 Raise_Exception(Ada.IO_Exceptions.Name_Error'Identity, 854 "PG06: Unable to open trace file " & Filename & " (Open_DB_Trace)."); 855 end if; 856 857 Open(C.Trace_Ada,Append_File,C.Trace_File,Form => "shared=yes"); 858 Ada.Text_IO.Put_Line(C.Trace_Ada,"-- Start of Trace, Mode = " & Trace_Mode_Type'Image(Mode)); 859 860 if Mode = Trace_DB or Mode = Trace_Full then 861 PQtrace(C.Connection,C.Trace_File); 862 end if; 863 864 end; 865 866 C.Trace_Filename := new String(1..Filename'Length); 867 C.Trace_Filename.all := Filename; 868 C.Trace_Mode := Mode; 869 C.Trace_On := True; -- Enabled by default until Set_Trace disables this 870 871 end Open_DB_Trace; 872 873 874 875 procedure Close_DB_Trace(C : in out Connection_Type) is 876 begin 877 878 if C.Trace_Mode = Trace_None then 879 return; -- No tracing in progress 880 end if; 881 882 pragma assert(C.Trace_Filename /= null); 883 884 declare 885 use CStr; 886 procedure PQuntrace(PGconn : PG_Conn); 887 pragma Import(C,PQuntrace,"PQuntrace"); 888 begin 889 if C.Trace_Mode = Trace_DB or C.Trace_Mode = Trace_Full then 890 PQuntrace(C.Connection); 891 end if; 892 893 Free(C.Trace_Filename); 894 895 Ada.Text_IO.Put_Line(C.Trace_Ada,"-- End of Trace."); 896 Ada.Text_IO.Close(C.Trace_Ada); -- This closes C.Trace_File too 897 898 C.Trace_Mode := Trace_None; 899 C.Trace_On := True; -- Restore default 900 end; 901 902 end Close_DB_Trace; 903 904 905 906 procedure Set_Trace(C : in out Connection_Type; Trace_On : Boolean := True) is 907 procedure PQtrace(PGconn : PG_Conn; debug_port : CStr.FILEs); 908 procedure PQuntrace(PGconn : PG_Conn); 909 pragma Import(C,PQtrace,"PQtrace"); 910 pragma Import(C,PQuntrace,"PQuntrace"); 911 912 Orig_Trace : Boolean := C.Trace_On; 913 begin 914 C.Trace_On := Set_Trace.Trace_On; 915 916 if Orig_Trace = C.Trace_On then 917 return; -- No change 918 end if; 919 920 if C.Trace_On then 921 if C.Trace_Mode = Trace_DB or C.Trace_Mode = Trace_Full then 922 PQtrace(C.Connection,C.Trace_File); -- Enable libpq tracing 923 end if; 924 else 925 if C.Trace_Mode = Trace_DB or C.Trace_Mode = Trace_Full then 926 PQuntrace(C.Connection); -- Disable libpq tracing 927 end if; 928 end if; 929 end Set_Trace; 930 931 932 933 function Is_Trace(C : Connection_Type) return Boolean is 934 begin 935 return C.Trace_On; 936 end Is_Trace; 937 938 939 940 function In_Abort_State(C : Connection_Type) return Boolean is 941 begin 942 if C.Connection = Null_Connection then 943 return False; 944 end if; 945 return C.Abort_State; 946 end In_Abort_State; 947 948 949 950 ------------------------------ 951 -- SQL QUERY API : 952 ------------------------------ 953 954 955 procedure Free(R : in out PQ_Result) is 956 procedure PQclear(R : PQ_Result); 957 pragma Import(C,PQclear,"PQclear"); 958 begin 959 if R /= Null_Result then 960 PQclear(R); 961 R := Null_Result; 962 end if; 963 end Free; 964 965 966 967 procedure Clear(Q : in out Query_Type) is 968 begin 969 Free(Q.Result); 970 Clear(Root_Query_Type(Q)); 971 end Clear; 972 973 974 975 procedure Append_Quoted(Q : in out Query_Type; Connection : Root_Connection_Type'Class; SQL : String; After : String := "") is 976 function PQescapeString(to, from : System.Address; length : size_t) return size_t; 977 pragma Import(C,PQescapeString,"PQescapeString"); 978 C_Length : size_t := SQL'Length * 2 + 1; 979 C_From : char_array := To_C(SQL); 980 C_To : char_array(0..C_Length-1); 981 R_Length : size_t := PQescapeString(C_To'Address,C_From'Address,C_Length); 982 begin 983 Append(Q,"'" & To_Ada(C_To) & "'",After); 984 Q.Caseless(Q.Count) := False; -- Preserve case for this one 985 end Append_Quoted; 986 987 988 989 procedure Execute(Query : in out Query_Type; Connection : in out Root_Connection_Type'Class) is 990 function PQexec(C : PG_Conn; Q : System.Address) return PQ_Result; 991 pragma Import(C,PQexec,"PQexec"); 992 R : Result_Type; 993 begin 994 995 Query.SQL_Case := Connection.SQL_Case; 996 997 if not Is_Connected(Connection) then 998 Raise_Exception(Not_Connected'Identity, 999 "PG14: The Connection_Type object supplied is not connected (Execute)."); 1000 end if; 1001 1002 if In_Abort_State(Connection) then 1003 Raise_Exception(Abort_State'Identity, 1004 "PG15: The PostgreSQL connection is in the Abort state (Execute)."); 1005 end if; 1006 1007 if Query.Result /= Null_Result then 1008 Free(Query.Result); 1009 end if; 1010 1011 declare 1012 A_Query : String := To_String(Query); 1013 C_Query : char_array := To_C(A_Query); 1014 begin 1015 if Connection.Trace_On then 1016 if Connection.Trace_Mode = Trace_APQ or Connection.Trace_Mode = Trace_Full then 1017 Ada.Text_IO.Put_Line(Connection.Trace_Ada,"-- SQL QUERY:"); 1018 Ada.Text_IO.Put_Line(Connection.Trace_Ada,A_Query); 1019 Ada.Text_IO.Put_Line(Connection.Trace_Ada,";"); 1020 end if; 1021 end if; 1022 1023 Query.Result := PQexec(Internal_Connection(Connection_Type(Connection)),C_Query'Address); 1024 1025 if Connection.Trace_On then 1026 if Connection.Trace_Mode = Trace_APQ or Connection.Trace_Mode = Trace_Full then 1027 Ada.Text_IO.Put_Line(Connection.Trace_Ada,"-- Result: '" & Command_Status(Query) & "'"); 1028 Ada.Text_IO.New_Line(Connection.Trace_Ada); 1029 end if; 1030 end if; 1031 end; 1032 1033 if Query.Result /= Null_Result then 1034 Query.Tuple_Index := First_Tuple_Index; 1035 R := Result(Query); 1036 if R /= Command_OK and R /= Tuples_OK then 1037-- if Connection.Trace_On then 1038-- Ada.Text_IO.Put_Line(Connection.Trace_Ada,"-- Error " & 1039-- Result_Type'Image(Query.Error_Code) & " : " & Error_Message(Query)); 1040-- end if; 1041 Raise_Exception(SQL_Error'Identity, 1042 "PG16: The query failed (Execute)."); 1043 end if; 1044 else 1045-- if Connection.Trace_On then 1046-- Ada.Text_IO.Put_Line(Connection.Trace_Ada,"-- Error " & 1047-- Result_Type'Image(Query.Error_Code) & " : " & Error_Message(Query)); 1048-- end if; 1049 Raise_Exception(SQL_Error'Identity, 1050 "PG17: The query failed (Execute)."); 1051 end if; 1052 1053 end Execute; 1054 1055 1056 1057 procedure Execute_Checked(Query : in out Query_Type; Connection : in out Root_Connection_Type'Class; Msg : String := "") is 1058 use Ada.Text_IO; 1059 begin 1060 begin 1061 Execute(Query,Connection); 1062 exception 1063 when Ex : SQL_Error => 1064 if Msg'Length > 0 then 1065 Put(Standard_Error,"*** SQL ERROR: "); 1066 Put_Line(Standard_Error,Msg); 1067 else 1068 Put(Standard_Error,"*** SQL ERROR IN QUERY:"); 1069 New_Line(Standard_Error); 1070 Put(Standard_Error,To_String(Query)); 1071 if Col(Standard_Error) > 1 then 1072 New_Line(Standard_Error); 1073 end if; 1074 end if; 1075 Put(Standard_Error,"["); 1076 Put(Standard_Error,Result_Type'Image(Result(Query))); 1077 Put(Standard_Error,": "); 1078 Put(Standard_Error,Error_Message(Query)); 1079 Put_Line(Standard_Error,"]"); 1080 Reraise_Occurrence(Ex); 1081 when Ex : others => 1082 Reraise_Occurrence(Ex); 1083 end; 1084 end Execute_Checked; 1085 1086 1087 1088 procedure Begin_Work(Query : in out Query_Type; Connection : in out Root_Connection_Type'Class) is 1089 begin 1090 if In_Abort_State(Connection) then 1091 Raise_Exception(Abort_State'Identity, 1092 "PG36: PostgreSQL connection is in the abort state (Begin_Work)."); 1093 end if; 1094 Clear(Query); 1095 Prepare(Query,"BEGIN WORK"); 1096 Execute(Query,Connection); 1097 Clear(Query); 1098 end Begin_Work; 1099 1100 1101 1102 procedure Commit_Work(Query : in out Query_Type; Connection : in out Root_Connection_Type'Class) is 1103 begin 1104 if In_Abort_State(Connection) then 1105 Raise_Exception(Abort_State'Identity, 1106 "PG37: PostgreSQL connection is in the abort state (Commit_Work)."); 1107 end if; 1108 Clear(Query); 1109 Prepare(Query,"COMMIT WORK"); 1110 Execute(Query,Connection); 1111 Clear(Query); 1112 end Commit_Work; 1113 1114 1115 1116 procedure Rollback_Work(Query : in out Query_Type; Connection : in out Root_Connection_Type'Class) is 1117 begin 1118 Clear(Query); 1119 Prepare(Query,"ROLLBACK WORK"); 1120 Execute(Query,Connection); 1121 Clear_Abort_State(Connection); 1122 Clear(Query); 1123 end Rollback_Work; 1124 1125 1126 1127 procedure Rewind(Q : in out Query_Type) is 1128 begin 1129 Q.Rewound := True; 1130 Q.Tuple_Index := First_Tuple_Index; 1131 end Rewind; 1132 1133 1134 1135 procedure Fetch(Q : in out Query_Type) is 1136 begin 1137 if not Q.Rewound then 1138 Q.Tuple_Index := Q.Tuple_Index + 1; 1139 else 1140 Q.Rewound := False; 1141 end if; 1142 Fetch(Q,Q.Tuple_Index); 1143 end Fetch; 1144 1145 1146 1147 procedure Fetch(Q : in out Query_Type; TX : Tuple_Index_Type) is 1148 NT : Tuple_Count_Type := Tuples(Q); -- May raise No_Result 1149 begin 1150 if NT < 1 then 1151 Raise_Exception(No_Tuple'Identity, 1152 "PG33: There is no row" & Tuple_Index_Type'Image(TX) & " (Fetch)."); 1153 end if; 1154 Q.Tuple_Index := TX; 1155 Q.Rewound := False; 1156 if TX > NT then 1157 Raise_Exception(No_Tuple'Identity, 1158 "PG34: There is no row" & Tuple_Index_Type'Image(TX) & " (Fetch)."); 1159 end if; 1160 end Fetch; 1161 1162 1163 1164 function End_of_Query(Q : Query_Type) return Boolean is 1165 NT : Tuple_Count_Type := Tuples(Q); -- May raise No_Result 1166 begin 1167 if NT < 1 then 1168 return True; -- There are no tuples to return 1169 end if; 1170 1171 if Q.Rewound then 1172 return False; -- There is at least 1 tuple to return yet 1173 end if; 1174 1175 return Tuple_Count_Type(Q.Tuple_Index) >= NT; -- We've fetched them all 1176 end End_of_Query; 1177 1178 1179 1180 function Tuple(Q : Query_Type) return Tuple_Index_Type is 1181 NT : Tuple_Count_Type := Tuples(Q); -- May raise No_Result 1182 begin 1183 if NT < 1 or else Q.Rewound then 1184 Raise_Exception(No_Tuple'Identity, 1185 "PG35: There are no tuples to return (Tuple)."); 1186 end if; 1187 return Q.Tuple_Index; 1188 end Tuple; 1189 1190 1191 1192 function Tuples(Q : Query_Type) return Tuple_Count_Type is 1193 use Interfaces.C; 1194 function PQntuples(R : PQ_Result) return int; 1195 pragma Import(C,PQntuples,"PQntuples"); 1196 begin 1197 if Q.Result = Null_Result then 1198 Raise_Exception(No_Result'Identity, 1199 "PG19: There are no query results (Tuples)."); 1200 end if; 1201 return Tuple_Count_Type(PQntuples(Q.Result)); 1202 end Tuples; 1203 1204 1205 1206 function Columns(Q : Query_Type) return Natural is 1207 use Interfaces.C; 1208 function PQnfields(R : PQ_Result) return int; 1209 pragma Import(C,PQnfields,"PQnfields"); 1210 begin 1211 if Q.Result = Null_Result then 1212 Raise_Exception(No_Result'Identity, 1213 "PG20: There are no query results (Columns)."); 1214 end if; 1215 return Natural(PQnfields(Q.Result)); 1216 end Columns; 1217 1218 1219 1220 function Column_Name(Q : Query_Type; CX : Column_Index_Type) return String is 1221 use Interfaces.C.Strings; 1222 function PQfname(R : PQ_Result; CBX : int) return chars_ptr; 1223 pragma Import(C,PQfname,"PQfname"); 1224 1225 CBX : int := int(CX) - 1; -- Make zero based 1226 begin 1227 if Q.Result = Null_Result then 1228 Raise_Exception(No_Result'Identity, 1229 "PG21: There are no query results (Column_Name)."); 1230 end if; 1231 declare 1232 use Interfaces.C.Strings; 1233 CP : chars_ptr := PQfname(Q.Result,CBX); 1234 begin 1235 if CP = Null_Ptr then 1236 Raise_Exception(No_Column'Identity, 1237 "PG22: There is no column CX=" & Column_Index_Type'Image(CX) & "."); 1238 end if; 1239 return To_Case(Value_Of(CP),Q.SQL_Case); 1240 end; 1241 end Column_Name; 1242 1243 1244 1245 function Column_Index(Q : Query_Type; Name : String) return Column_Index_Type is 1246 use Interfaces.C.Strings; 1247 function PQfnumber(R : PQ_Result; CBX : System.Address) return int; 1248 pragma Import(C,PQfnumber,"PQfnumber"); 1249 1250 C_Name : char_array := To_C(Name); 1251 CBX : int := -1; 1252 begin 1253 if Q.Result = Null_Result then 1254 Raise_Exception(No_Result'Identity, 1255 "PG23: There are no query results (Column_Index)."); 1256 end if; 1257 CBX := PQfnumber(Q.Result,C_Name'Address); 1258 if CBX < 0 then 1259 Raise_Exception(No_Column'Identity, 1260 "PG24: There is no column named '" & Name & " (Column_Index)."); 1261 end if; 1262 return Column_Index_Type(CBX+1); 1263 end Column_Index; 1264 1265 1266 1267 function Is_Column(Q : Query_Type; CX : Column_Index_Type) return Boolean is 1268 begin 1269 if Q.Result = Null_Result then 1270 return False; 1271 end if; 1272 return Natural(CX) <= Columns(Q); 1273 end Is_Column; 1274 1275 1276 1277 function Column_Type(Q : Query_Type; CX : Column_Index_Type) return Row_ID_Type is 1278 function PQftype(R : PQ_Result; Field_Index : int) return PQOid_Type; 1279 pragma Import(C,PQftype,"PQftype"); 1280 CBX : int := int(CX) - 1; 1281 begin 1282 if Q.Result = Null_Result then 1283 Raise_Exception(No_Result'Identity, 1284 "PG25: There are no query results (Column_Type)."); 1285 end if; 1286 if not Is_Column(Q,CX) then 1287 Raise_Exception(No_Column'Identity, 1288 "PG26: There is no column CX=" & Column_Index_Type'Image(CX) & " (Column_Type)."); 1289 end if; 1290 return Row_ID_Type(PQftype(Q.Result,CBX)); 1291 end Column_Type; 1292 1293 1294 1295 function Is_Null(Q : Query_Type; CX : Column_Index_Type) return Boolean is 1296 use Interfaces.C.Strings; 1297 function PQgetisnull(R : PQ_Result; tup_num, field_num : int) return int; 1298 pragma Import(C,PQgetisnull,"PQgetisnull"); 1299 C_TX : int := int(Q.Tuple_Index) - 1; -- Make zero based tuple # 1300 C_CX : int := int(CX) - 1; -- Field index 1301 begin 1302 if Q.Result = Null_Result then 1303 Raise_Exception(No_Result'Identity, 1304 "PG31: There are no query results (Is_Null)."); 1305 end if; 1306 if not Is_Column(Q,CX) then 1307 Raise_Exception(No_Column'Identity, 1308 "PG32: There is now column" & Column_Index_Type'Image(CX) & " (Is_Null)."); 1309 end if; 1310 return PQgetisnull(Q.Result,C_TX,C_CX) /= 0; 1311 end Is_Null; 1312 1313 1314 1315 function Value(Query : Query_Type; CX : Column_Index_Type) return String is 1316 use Interfaces.C.Strings; 1317 function PQgetvalue(R : PQ_Result; tup_num, field_num : int) return chars_ptr; 1318 pragma Import(C,PQgetvalue,"PQgetvalue"); 1319 function PQgetisnull(R : PQ_Result; tup_num, field_num : int) return int; 1320 pragma Import(C,PQgetisnull,"PQgetisnull"); 1321 C_TX : int := int(Query.Tuple_Index) - 1; -- Make zero based tuple # 1322 C_CX : int := int(CX) - 1; -- Field index 1323 begin 1324 if Query.Result = Null_Result then 1325 Raise_Exception(No_Result'Identity, 1326 "PG27: There are no query results (Value)."); 1327 end if; 1328 if not Is_Column(Query,CX) then 1329 Raise_Exception(No_Column'Identity, 1330 "PG28: There is no column CX=" & Column_Index_Type'Image(CX) & " (Value)."); 1331 end if; 1332 declare 1333 use Ada.Strings, Ada.Strings.Fixed; 1334 1335 C_Val : chars_ptr := PQgetvalue(Query.Result,C_TX,C_CX); 1336 begin 1337 if C_Val = Null_Ptr then 1338 Raise_Exception(No_Tuple'Identity, 1339 "PG29: There is no row" & Tuple_Index_Type'Image(Query.Tuple_Index) & " (Value)."); 1340 elsif PQgetisnull(Query.Result,C_TX,C_CX) /= 0 then 1341 Raise_Exception(Null_Value'Identity, 1342 "PG30: Value for column" & Column_Index_Type'Image(CX) & " is NULL (Value)."); 1343 else 1344 return Trim(Value_Of(C_Val),Right); 1345 end if; 1346 end; 1347 1348 end Value; 1349 1350 1351 1352 function Result(Query : Query_Type) return Natural is 1353 begin 1354 return Result_Type'Pos(Result(Query)); 1355 end Result; 1356 1357 1358 1359 function Result(Query : Query_Type) return Result_Type is 1360 function PQresultStatus(R : PQ_Result) return Result_Type; 1361 pragma Import(C,PQresultStatus,"PQresultStatus"); 1362 begin 1363 if Query.Result = Null_Result then 1364 Raise_Exception(No_Result'Identity, 1365 "PG13: There are no query results (function Result)."); 1366 end if; 1367 return PQresultStatus(Query.Result); 1368 end Result; 1369 1370 1371 1372 function Command_Oid(Query : Query_Type) return Row_ID_Type is 1373 function PQoidValue(R : PQ_Result) return PQOid_Type; 1374 pragma Import(C,PQoidValue,"PQoidValue"); 1375 begin 1376 1377 if Query.Result = Null_Result then 1378 Raise_Exception(No_Result'Identity, 1379 "PG12: There are no query results (Command_Oid)."); 1380 end if; 1381 1382 return Row_ID_Type(PQoidValue(Query.Result)); 1383 end Command_Oid; 1384 1385 1386 1387 function Null_Oid(Query : Query_Type) return Row_ID_Type is 1388 begin 1389 return APQ.PostgreSQL.Null_Row_ID; 1390 end Null_Oid; 1391 1392 1393 1394 function Command_Status(Query : Query_Type) return String is 1395 use Interfaces.C.Strings; 1396 function PQcmdStatus(R : PQ_Result) return chars_ptr; 1397 pragma Import(C,PQcmdStatus,"PQcmdStatus"); 1398 begin 1399 1400 if Query.Result = Null_Result then 1401 Raise_Exception(No_Result'Identity, 1402 "PG11: There are no query results (Command_Status)."); 1403 end if; 1404 1405 declare 1406 use Interfaces.C.Strings; 1407 Msg_Ptr : chars_ptr := PQcmdStatus(Query.Result); 1408 begin 1409 if Msg_Ptr = Null_Ptr then 1410 return ""; 1411 else 1412 return Strip_NL(Value_Of(Msg_Ptr)); 1413 end if; 1414 end; 1415 end Command_Status; 1416 1417 1418 1419 1420 function Error_Message(Query : Query_Type) return String is 1421 use Interfaces.C.Strings; 1422 function PQresultErrorMessage(R : PQ_Result) return chars_ptr; 1423 pragma Import(C,PQresultErrorMessage,"PQresultErrorMessage"); 1424 begin 1425 if Query.Result = Null_Result then 1426 Raise_Exception(No_Result'Identity, 1427 "PG10: There are no query results (Error_Message)."); 1428 end if; 1429 1430 declare 1431 use Interfaces.C.Strings; 1432 Msg_Ptr : chars_ptr := PQresultErrorMessage(Query.Result); 1433 begin 1434 if Msg_Ptr = Null_Ptr then 1435 return ""; 1436 else 1437 return Strip_NL(Value_Of(Msg_Ptr)); 1438 end if; 1439 end; 1440 end Error_Message; 1441 1442 1443 1444 function Is_Duplicate_Key(Query : Query_Type) return Boolean is 1445 Msg : String := Error_Message(Query); 1446 Dup : constant String := "ERROR: Cannot insert a duplicate key"; 1447 begin 1448 if Msg'Length < Dup'Length then 1449 return False; 1450 end if; 1451 return Msg(Msg'First..Msg'First+Dup'Length-1) = Dup; 1452 end Is_Duplicate_Key; 1453 1454 1455 1456 function Engine_Of(Q : Query_Type) return Database_Type is 1457 begin 1458 return Engine_PostgreSQL; 1459 end Engine_Of; 1460 1461 1462 -------------------------------------------------- 1463 -- BLOB SUPPORT : 1464 -------------------------------------------------- 1465 1466 function lo_creat(conn : PG_Conn; Mode : Mode_Type) return PQOid_Type; 1467 pragma Import(C,lo_creat,"lo_creat"); 1468 1469 function lo_open(conn : PG_Conn; Oid : PQOid_Type; Mode : Mode_Type) return Blob_Fd; 1470 pragma Import(C,lo_open,"lo_open"); 1471 1472 function lo_close(conn : PG_Conn; fd : Blob_Fd) return int; 1473 pragma Import(C,lo_close,"lo_close"); 1474 1475 function lo_read(conn : PG_Conn; fd : Blob_Fd; buf : System.Address; len : size_t) return int; 1476 pragma Import(C,lo_read,"lo_read"); 1477 1478 function lo_write(conn : PG_Conn; fd : Blob_Fd; buf : System.Address; len : size_t) return int; 1479 pragma Import(C,lo_write,"lo_write"); 1480 1481 function lo_unlink(conn : PG_Conn; Oid : PQOid_Type) return int; 1482 pragma Import(C,lo_unlink,"lo_unlink"); 1483 1484 function lo_lseek(conn : PG_Conn; fd : Blob_Fd; offset, whence : int) return int; 1485 pragma Import(C,lo_lseek,"lo_lseek"); 1486 1487 procedure Free is new Ada.Unchecked_Deallocation(Blob_Object,Blob_Type); 1488 1489 1490 -- internal 1491 1492 function Raw_Index(Blob : Blob_Type) return Str.Stream_Element_Offset is 1493 use Ada.Streams; 1494 Offset : int; 1495 begin 1496 loop -- In loop form in case EINTR processing should be required someday 1497 Offset := lo_lseek(Blob.Conn.Connection,Blob.Fd,0,Seek_Cur); 1498 exit when Offset >= 0; 1499 Raise_Exception(Blob_Error'Identity, 1500 "PG38: Server blob error occurred."); 1501 end loop; 1502 1503 return Stream_Element_Offset(Offset + 1); 1504 end Raw_Index; 1505 1506 1507 1508 1509 procedure Raw_Set_Index(Blob : Blob_Object; To : Str.Stream_Element_Offset) is 1510 Offset : int := int(To) - 1; 1511 Z : int; 1512 begin 1513 loop -- In loop form in case EINTR processing should be required someday 1514 Z := lo_lseek(Blob.Conn.Connection,Blob.Fd,Offset,Seek_Set); 1515 exit when Z >= 0; 1516 Raise_Exception(Blob_Error'Identity, 1517 "PG39: Server blob error occurred."); 1518 end loop; 1519 end Raw_Set_Index; 1520 1521 1522 1523 function Internal_Size(Blob : Blob_Type) return Str.Stream_Element_Offset is 1524 use Ada.Streams; 1525 Saved_Pos : Stream_Element_Offset := Raw_Index(Blob); 1526 End_Offset : int := lo_lseek(Blob.Conn.Connection,Blob.Fd,0,Seek_End); 1527 begin 1528 if End_Offset < 0 then 1529 Raise_Exception(Blob_Error'Identity, 1530 "PG40: Server blob error occurred."); 1531 end if; 1532 Raw_Set_Index(Blob.all,Saved_Pos); 1533 return Stream_Element_Offset(End_Offset); 1534 end Internal_Size; 1535 1536 1537 1538 procedure Internal_Write( 1539 Stream: in out Blob_Object; 1540 Item: in Ada.Streams.Stream_Element_Array 1541 ) is 1542 use Ada.Streams; 1543 Total : size_t := 0; 1544 Len : size_t; 1545 IX : Stream_Element_Offset := Item'First; 1546 N : int; 1547 begin 1548 while IX < Item'Last loop 1549 Len := size_t(Item'Last - IX + 1); 1550 N := lo_write(Stream.Conn.Connection,Stream.Fd,Item(IX)'Address,Len); 1551 if N < 0 then 1552 Raise_Exception(Blob_Error'Identity, 1553 "PG43: Server blob write error occurred."); 1554 elsif N > 0 then 1555 IX := IX + Stream_Element_Offset(N); 1556 1557 Stream.Phy_Offset := Stream.Phy_Offset + Stream_Element_Offset(N); 1558 if Stream.Phy_Offset - 1 > Stream.The_Size then 1559 Stream.The_Size := Stream.Phy_Offset - 1; 1560 end if; 1561 end if; 1562 1563 if N = 0 then 1564 Raise_Exception(Ada.IO_Exceptions.End_Error'Identity, 1565 "PG44: End_Error raised while server was writing blob."); 1566 end if; 1567 end loop; 1568 1569 end Internal_Write; 1570 1571 1572 1573 procedure Internal_Read( 1574 Stream: in out Blob_Object; 1575 Item: out Ada.Streams.Stream_Element_Array; 1576 Last: out Ada.Streams.Stream_Element_Offset 1577 ) is 1578 use Ada.Streams; 1579 1580 Len : size_t := size_t(Item'Length); 1581 N : int; 1582 begin 1583 1584 loop -- In loop form in case EINTR processing should be required someday 1585 N := lo_read(Stream.Conn.Connection,Stream.Fd,Item(Item'First)'Address,Len); 1586 exit when N >= 0; 1587 Raise_Exception(Blob_Error'Identity, 1588 "PG41: Server blob error occurred while reading the blob."); 1589 end loop; 1590 1591 if N = 0 then 1592 Raise_Exception(Ada.IO_Exceptions.End_Error'Identity, 1593 "PG42: Reached the end of blob while reading."); 1594 end if; 1595 1596 Last := Item'First + Stream_Element_Offset(N) - 1; 1597 Stream.Phy_Offset := Stream.Phy_Offset + Stream_Element_Offset(N); 1598 1599 end Internal_Read; 1600 1601 1602 1603 procedure Internal_Blob_Open(Blob : in out Blob_Type; Mode : Mode_Type; Buf_Size : Natural := Buf_Size_Default) is 1604 use Ada.Streams; 1605 begin 1606 Blob.Mode := Internal_Blob_Open.Mode; 1607 Blob.Fd := lo_open(Blob.Conn.Connection,PQOid_Type(Blob.Oid),Blob.Mode); 1608 if Blob.Fd = -1 then 1609 Free(Blob); 1610 Raise_Exception(Blob_Error'Identity, 1611 "PG45: Unable to open blob on server (OID=" & Row_ID_Type'Image(Blob.Oid) & ")."); 1612 end if; 1613 if Buf_Size > 0 then 1614 Blob.Buffer := new Stream_Element_Array(1..Stream_Element_Offset(Buf_Size)); 1615 Blob.Buf_Empty := True; 1616 Blob.Buf_Dirty := False; 1617 Blob.Buf_Offset := 0; 1618 Blob.Log_Offset := 1; 1619 Blob.Phy_Offset := 1; 1620 Blob.The_Size := Stream_Element_Offset(Internal_Size(Blob)); 1621 else 1622 null; -- unbuffered blob operations will be used 1623 end if; 1624 end Internal_Blob_Open; 1625 1626 1627 1628 procedure Internal_Set_Index(Blob : in out Blob_Object; To : Str.Stream_Element_Offset) is 1629 use Ada.Streams; 1630 begin 1631 if Blob.Phy_Offset /= Stream_Element_Offset(To) then 1632 Raw_Set_Index(Blob,To); 1633 Blob.Phy_Offset := Stream_Element_Offset(To); 1634 end if; 1635 end Internal_Set_Index; 1636 1637 1638 1639 -- end internal 1640 1641 1642 1643 function Blob_Create(DB : access Connection_Type; Buf_Size : Natural := Buf_Size_Default) return Blob_Type is 1644 Blob : Blob_Type; 1645 begin 1646 Blob := new Blob_Object(DB); 1647 Blob.Oid := Row_ID_Type(lo_creat(Blob.Conn.Connection,Read_Write)); 1648 if Blob.Oid = -1 then 1649 free(Blob); 1650 Raise_Exception(Blob_Error'Identity, 1651 "PG46: Unable to create blob on server."); 1652 end if; 1653 1654 begin 1655 Internal_Blob_Open(Blob,Write,Buf_Size); 1656 exception 1657 when Ex : others => 1658 Blob_Unlink(DB.all,Blob.Oid); -- Release what will result in an unused blob! 1659 Reraise_Occurrence(Ex); -- HINT: Internal_Blob_Open() FAILS IF IT IS NOT IN A TRANSACTION! 1660 end; 1661 1662 return Blob; 1663 end Blob_Create; 1664 1665 1666 1667 function Blob_Open(DB : access Connection_Type; Oid : Row_ID_Type; Mode : Mode_Type; Buf_Size : Natural := Buf_Size_Default) return Blob_Type is 1668 Blob : Blob_Type; 1669 begin 1670 Blob := new Blob_Object(DB); 1671 Blob.Oid := Blob_Open.Oid; 1672 Internal_Blob_Open(Blob,Mode,Buf_Size); 1673 return Blob; 1674 end Blob_Open; 1675 1676 1677 1678 procedure Blob_Flush(Blob : in out Blob_Object) is 1679 begin 1680 if Blob.Buffer /= null then 1681 if ( not Blob.Buf_Empty ) and Blob.Buf_Dirty then 1682 Internal_Set_Index(Blob,Blob.Buf_Offset); 1683 Internal_Write(Blob,Blob.Buffer(1..Blob.Buf_Size)); 1684 end if; 1685 Blob.Buf_Dirty := False; 1686 else 1687 null; -- Ignore flush calls in the unbuffered case 1688 end if; 1689 end Blob_Flush; 1690 1691 1692 1693 procedure Blob_Flush(Blob : Blob_Type) is 1694 begin 1695 Blob_Flush(Blob.all); 1696 end Blob_Flush; 1697 1698 1699 1700 procedure Internal_Blob_Close(Blob : in out Blob_Object) is 1701 Z : int; 1702 begin 1703 if Blob.Buffer /= null then 1704 if Blob.Buf_Dirty then 1705 Blob_Flush(Blob); 1706 end if; 1707 Free(Blob.Buffer); 1708 end if; 1709 1710 Z := lo_close(Blob.Conn.Connection,Blob.Fd); 1711 if Z /= 0 then 1712 Raise_Exception(Blob_Error'Identity, 1713 "PG47: Server error when closing blob."); 1714 end if; 1715 Blob.Fd := -1; 1716 end Internal_Blob_Close; 1717 1718 1719 1720 procedure Blob_Close(Blob : in out Blob_Type) is 1721 begin 1722 Internal_Blob_Close(Blob.all); 1723 Free(Blob); 1724 end Blob_Close; 1725 1726 1727 1728 procedure Blob_Set_Index(Blob : Blob_Type; To : Blob_Offset) is 1729 use Ada.Streams; 1730 begin 1731 if Blob.Buffer /= null then 1732 Blob.Log_Offset := Stream_Element_Offset(To); 1733 else 1734 Internal_Set_Index(Blob.all,Stream_Element_Offset(To)); 1735 end if; 1736 end Blob_Set_Index; 1737 1738 1739 1740 function Internal_Index(Blob : Blob_Type) return Str.Stream_Element_Offset is 1741 begin 1742 return Blob.Phy_Offset; 1743 end Internal_Index; 1744 1745 1746 1747 function Blob_Index(Blob : Blob_Type) return Blob_Offset is 1748 begin 1749 if Blob.Buffer /= null then 1750 return Blob_Offset(Blob.Log_Offset); 1751 else 1752 return Blob_Offset(Internal_Index(Blob)); 1753 end if; 1754 end Blob_Index; 1755 1756 1757 1758 function End_of_Blob(Blob : Blob_Type) return Boolean is 1759 use Ada.Streams; 1760 begin 1761 if Blob.Buffer /= null then 1762 return Blob.Log_Offset > Blob.The_Size; 1763 else 1764 return Blob_Index(Blob) > Blob_Size(Blob); 1765 end if; 1766 end End_of_Blob; 1767 1768 1769 1770 function Blob_Oid(Blob : Blob_Type) return Row_ID_Type is 1771 begin 1772 return Blob.Oid; 1773 end Blob_Oid; 1774 1775 1776 1777 function Blob_Size(Blob : Blob_Type) return Blob_Count is 1778 begin 1779 if Blob.Buffer /= null then 1780 return Blob_Count(Blob.The_Size); 1781 else 1782 return Blob_Count(Internal_Size(Blob)); 1783 end if; 1784 end Blob_Size; 1785 1786 1787 1788 function Blob_Stream(Blob : Blob_Type) return Root_Stream_Access is 1789 begin 1790 if Blob = Null then 1791 Raise_Exception(Blob_Error'Identity, 1792 "PG49: No blob to create a stream from (Blob_Stream)."); 1793 end if; 1794 return Root_Stream_Access(Blob); 1795 end Blob_Stream; 1796 1797 1798 1799 procedure Blob_Unlink(DB : Connection_Type; Oid : Row_ID_Type) is 1800 Z : int; 1801 begin 1802 Z := lo_unlink(DB.Connection,PQOid_Type(Oid)); 1803 if Z = -1 then 1804 Raise_Exception(Blob_Error'Identity, 1805 "PG50: Unable to unlink blob OID=" & Row_ID_Type'Image(Oid) & " (Blob_Unlink)."); 1806 end if; 1807 end Blob_Unlink; 1808 1809 1810 1811 function lo_import(conn : PG_Conn; filename : System.Address) return int; 1812 pragma Import(C,lo_import,"lo_import"); 1813 1814 function lo_export(conn : PG_Conn; Oid : PQOid_Type; filename : System.Address) return int; 1815 pragma Import(C,lo_export,"lo_export"); 1816 1817 1818 procedure Blob_Import(DB : Connection_Type; Pathname : String; Oid : out Row_ID_Type) is 1819 use Interfaces.C; 1820 P : char_array := To_C(Pathname); 1821 Z : int; 1822 begin 1823 Oid := Row_ID_Type'Last; 1824 Z := lo_import(DB.Connection,P'Address); 1825 if Z <= -1 then 1826 Raise_Exception(Blob_Error'Identity, 1827 "PG51: Unable to import blob from " & Pathname & " (Blob_Import)."); 1828 end if; 1829 Oid := Row_ID_Type(Z); 1830 end Blob_Import; 1831 1832 1833 1834 procedure Blob_Export(DB : Connection_Type; Oid : Row_ID_Type; Pathname : String) is 1835 P : char_array := To_C(Pathname); 1836 Z : int; 1837 begin 1838 Z := lo_export(DB.Connection,PQOid_Type(Oid),P'Address); 1839 if Z <= -1 then 1840 Raise_Exception(Blob_Error'Identity, 1841 "PG52: Unable to export blob to " & Pathname & " (Blob_Export)."); 1842 end if; 1843 end Blob_Export; 1844 1845 1846 1847 function Generic_Blob_Open(DB : access Connection_Type; Oid : Oid_Type; Mode : Mode_Type; Buf_Size : Natural := Buf_Size_Default) return Blob_Type is 1848 begin 1849 return Blob_Open(DB,Row_ID_Type(Oid),Mode,Buf_Size); 1850 end Generic_Blob_Open; 1851 1852 1853 1854 function Generic_Blob_Oid(Blob : Blob_Type) return Oid_Type is 1855 begin 1856 return Oid_Type(Blob_Oid(Blob)); 1857 end Generic_Blob_Oid; 1858 1859 1860 1861 procedure Generic_Blob_Unlink(DB : Connection_Type; Oid : Oid_Type) is 1862 begin 1863 Blob_Unlink(DB,Row_ID_Type(Oid)); 1864 end Generic_Blob_Unlink; 1865 1866 1867 1868 procedure Generic_Blob_Import(DB : Connection_Type; Pathname : String; Oid : out Oid_Type) is 1869 Local_Oid : Row_ID_Type; 1870 begin 1871 Blob_Import(DB,Pathname,Local_Oid); 1872 Oid := Oid_Type(Local_Oid); 1873 end Generic_Blob_Import; 1874 1875 1876 1877 procedure Generic_Blob_Export(DB : Connection_Type; Oid : Oid_Type; Pathname : String) is 1878 begin 1879 Blob_Export(DB,Row_ID_Type(Oid),Pathname); 1880 end Generic_Blob_Export; 1881 1882 1883 1884-- private 1885 1886 1887 --------------------- 1888 -- CONNECTION_TYPE -- 1889 --------------------- 1890 1891 1892 procedure Initialize(C : in out Connection_Type) is 1893 begin 1894 C.Port_Format := IP_Port; 1895 C.Port_Number := 5432; 1896 C.keyname_val_cache_uptodate := false; 1897 1898 end Initialize; 1899 1900 1901 1902 procedure Finalize(C : in out Connection_Type) is 1903 begin 1904 Internal_Reset(C,In_Finalize => True); 1905 end Finalize; 1906 1907 1908 1909 function Internal_Connection(C : Connection_Type) return PG_Conn is 1910 begin 1911 return C.Connection; 1912 end Internal_Connection; 1913 1914 1915 1916 function Query_Factory( C: in Connection_Type ) return Root_Query_Type'Class is 1917 q: Query_Type; 1918 begin 1919 return q; 1920 end query_factory; 1921 1922 1923 1924 ---------------- 1925 -- QUERY_TYPE -- 1926 ---------------- 1927 1928 1929 procedure Adjust(Q : in out Query_Type) is 1930 begin 1931 Q.Result := Null_Result; 1932 Adjust(Root_Query_Type(Q)); 1933 end Adjust; 1934 1935 1936 1937 procedure Finalize(Q : in out Query_Type) is 1938 begin 1939 Clear(Q); 1940 end Finalize; 1941 1942 1943 1944 function SQL_Code(Query : Query_Type) return SQL_Code_Type is 1945 begin 1946 return 0; 1947 end SQL_Code; 1948 1949 1950 1951 --------------- 1952 -- BLOB_TYPE -- 1953 --------------- 1954 1955 1956 procedure Finalize(Blob : in out Blob_Object) is 1957 begin 1958 if Blob.Fd /= -1 then 1959 Internal_Blob_Close(Blob); 1960 end if; 1961 end Finalize; 1962 1963 1964 1965 procedure Read( 1966 Stream: in out Blob_Object; 1967 Item: out Ada.Streams.Stream_Element_Array; 1968 Last: out Ada.Streams.Stream_Element_Offset 1969 ) is 1970 use Ada.Streams; 1971 1972 IX : Stream_Element_Offset := Item'First; 1973 BX : Stream_Element_Offset; 1974 begin 1975 1976 if Stream.Buffer /= null then 1977 while IX <= Item'Last and Stream.Log_Offset <= Stream.The_Size loop 1978 1979 if ( not Stream.Buf_Empty ) and then Stream.Buf_Dirty then -- if not empty and is dirty 1980 if Stream.Log_Offset < Stream.Buf_Offset -- if offset too low 1981 or else Stream.Log_Offset >= Stream.Buf_Offset + Stream.Buf_Size then -- or offset too high 1982 Blob_Flush(Stream); 1983 Stream.Buf_Empty := True; 1984 end if; 1985 end if; 1986 1987 if Stream.Buf_Empty then -- If we have an empty buffer then.. 1988 if Stream.Log_Offset > Stream.The_Size + 1 then 1989 Raise_Exception(Ada.IO_Exceptions.End_Error'Identity, 1990 "PG47: End reached while reading blob."); 1991 end if; 1992 1993 Stream.Buf_Offset := Stream.Log_Offset; -- Start with our convenient offset 1994 Stream.Buf_Size := Stream.Buffer.all'Length; -- Try to read entire buffer in 1995 if Stream.Buf_Offset + Stream.Buf_Size - 1 > Stream.The_Size then 1996 Stream.Buf_Size := Stream.The_Size + 1 - Stream.Buf_Offset; -- read somewhat less in 1997 end if; 1998 Internal_Set_Index(Stream,Stream.Buf_Offset); 1999 Internal_Read(Stream,Stream.Buffer(1..Stream.Buf_Size),Last); 2000 if Last /= Stream.Buf_Size then -- Check that all was read 2001 Raise_Exception(Blob_Error'Identity, 2002 "PG48: Error while reading from blob."); 2003 end if; 2004 Stream.Buf_Empty := False; -- Buffer is not empty 2005 pragma assert(Stream.Buf_Dirty = False); -- Should not be dirty at this point 2006 BX := Stream.Buffer.all'First; -- Start reading from buffer here 2007 else 2008 BX := Stream.Log_Offset - Stream.Buf_Offset + Stream.Buffer.all'First; 2009 end if; 2010 2011 Item(IX) := Stream.Buffer.all(BX); -- Read item byte 2012 IX := IX + 1; -- Advance item index 2013 Stream.Log_Offset := Stream.Log_Offset + 1; -- Advance logical offset 2014 end loop; 2015 Last := IX - 1; 2016 else 2017 Internal_Read(Stream,Item,Last); 2018 end if; 2019 end Read; 2020 2021 2022 2023 procedure Write( 2024 Stream: in out Blob_Object; 2025 Item: in Ada.Streams.Stream_Element_Array 2026 ) is 2027 use Ada.Streams; 2028 2029 IX : Stream_Element_Offset := Item'First; 2030 BX : Stream_Element_Offset := -1; 2031 begin 2032 2033 if Stream.Buffer /= null then 2034 while IX <= Item'Last loop 2035 if ( not Stream.Buf_Empty ) and then Stream.Buf_Dirty then -- Buffer is not empty and is dirty 2036 if Stream.Log_Offset < Stream.Buf_Offset -- if offset too low 2037 or else Stream.Log_Offset > Stream.Buf_Offset + Stream.Buf_Size -- or offset too high 2038 or else Stream.Buf_Size >= Stream.Buffer.all'Length then -- or buffer is full then.. 2039 Blob_Flush(Stream); -- Flush out dirty data 2040 Stream.Buf_Empty := True; -- Now mark buffer as empty 2041 else 2042 BX := Stream.Log_Offset - Stream.Buf_Offset + Stream.Buffer.all'First; 2043 end if; 2044 else 2045 BX := Stream.Log_Offset - Stream.Buf_Offset + Stream.Buffer.all'First; 2046 end if; 2047 2048 if Stream.Buf_Empty then -- if buf was empty or was just made empty then.. 2049 Stream.Buf_Offset := Stream.Log_Offset; -- Set to our convenient offset 2050 Stream.Buf_Size := 0; -- No data in this buffer yet 2051 Stream.Buf_Dirty := False; -- Make sure it's not marked dirty yet 2052 BX := Stream.Buffer.all'First; -- Point to start of buffer 2053 end if; 2054 2055 Stream.Buffer.all(BX) := Item(IX); -- Write the byte 2056 IX := IX + 1; -- Advance Item Index 2057 Stream.Log_Offset := Stream.Log_Offset + 1; -- Advance the logical blob offset 2058 Stream.Buf_Empty := False; -- Buffer is no longer empty 2059 Stream.Buf_Dirty := True; -- Buffer has been modified 2060 2061 if BX > Stream.Buf_Size then -- Did the buffer contents grow? 2062 Stream.Buf_Size := Stream.Buf_Size + 1; -- Buffer size has grown 2063 end if; 2064 end loop; 2065 else 2066 Internal_Write(Stream,Item); 2067 end if; 2068 end Write; 2069 2070 2071begin 2072 2073 declare 2074 use Ada.Calendar; 2075 begin 2076 No_Date := Time_Of(Year_Number'First,Month_Number'First,Day_Number'First); 2077 end; 2078 2079end APQ.PostgreSQL.Client; 2080 2081-- End $Source: /cvsroot/apq/apq/apq-postgresql-client.adb,v $ 2082