Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 66 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
66
Dung lượng
1,82 MB
Nội dung
CHAPTER 14 ■ ACCESSING POSTGRESQL FROM C USING EMBEDDED SQL 439 Handling Empty Results So far in our sample programs in this chapter, we have been careful to make sure that when we retrieve data, we obtain exactly one row. The column values are then extracted into host variables for our program to use. By making a small change to our select program to look up customers according to ZIP code, we can demonstrate handling cases where no rows are returned. The following sample program (select2.pgc) detects the case where no data is returned from a SELECT: #include <stdio.h> #include <string.h> #include <stdlib.h> EXEC SQL include sqlca; EXEC SQL whenever sqlwarning sqlprint; EXEC SQL whenever sqlerror do GiveUp(); void GiveUp() { fprintf(stderr, "Fatal Error\n"); sqlprint(); exit(1); } main(int argc, char *argv[]) { EXEC SQL BEGIN declare section; int id; char title[4]; int title_ind; char zip[10]; varchar lname[32]; varchar town[64]; int town_ind; EXEC SQL END declare section; if(argc != 2) { printf("Usage: select zipcode\n"); exit(1); } strncpy(zip, argv[1], sizeof(zip)); EXEC SQL CONNECT TO bpfinal; MatthewStones_4789.book Page 439 Wednesday, February 23, 2005 6:49 AM 440 CHAPTER 14 ■ ACCESSING POSTGRESQL FROM C USING EMBEDDED SQL EXEC SQL SELECT customer_id, title, lname, town into :id, :title:title_ind, :lname, :town:town_ind FROM customer WHERE zipcode = :zip; if(sqlca.sqlerrd[2] == 0) { printf("no customer found\n"); } else { printf("title is%sNULL\n", title_ind? " ": " not "); printf("town is%sNULL\n", town_ind? " ": " not "); printf("customer id: %d\n", id); printf("%.*s %.*s <%.*s>\n", sizeof(title), title, lname.len, lname.arr, town.len, town.arr); } EXEC SQL DISCONNECT ALL; } In this program, we use the fact the SQL control area will contain information about the number of rows returned in sqlca.sqlerrd[2]. If this is zero, then we found no rows. Let’s use the program to query some data. $ make select2 $ ./select2 "NT2 2TX" title is not NULL town is not NULL customer id: 3 Miss Matthew <Nicetown> $ ./select2 "BG4 2XE" no customer found $ ./select2 "BG4 2WE" Fatal Error sql error SQL error #-203 in line 37. $ When we specify a zipcode search, where we find a customer with a zipcode that matches, we print out the details. Where there are no corresponding records, we get no records returned. The query detects this and prints a suitable message. Unfortunately, the third run showed that our program is not yet sufficiently robust! We chose a zipcode that belonged to more than one customer, and this caused an error. In this case, two customers have the same zipcode. As we cannot store details about both customers in our host variables, the program aborted, displaying an error message. To solve the problem of multiple rows being returned, we need to use a cursor, much as we did in the previous chapter. This is the subject of the next section. MatthewStones_4789.book Page 440 Wednesday, February 23, 2005 6:49 AM CHAPTER 14 ■ ACCESSING POSTGRESQL FROM C USING EMBEDDED SQL 441 Implementing Cursors in Embedded SQL In general, if you cannot guarantee that your query will return a single row, the sensible approach is to use a cursor. We saw these in the previous chapter using libpq. Now let’s see how to use cursors with ecpg. In case you skipped the previous chapter, it is worth reiterating the advice given there. In general, you should avoid writing code that assumes a single row or no rows are returned from a SELECT statement, unless that statement is a simple aggregate, such as a SELECT count(*) FROM type query, or a SELECT on a primary key, where you can guarantee the result will always be exactly one row. When in doubt, use a cursor. To deal with multiple rows being returned from a query, we retrieve them one at a time using a FETCH, with the column values being received into host variables in the same way as we have seen for single-row SELECT statements. As with the libpq library, we declare a cursor to be used to scroll through a collection of returned rows. The cursor acts as our bookmark, and we fetch the rows until no more data is available. To use a cursor, we must declare it and specify the query that it relates to. We may use a cursor declaration only within a PostgreSQL transaction, even if the cursor does not update the database: EXEC SQL BEGIN; EXEC SQL declare mycursor cursor for SELECT ; The SELECT statement that we use to define the cursor may contain host variables for conditions in WHERE clauses and so on, but it does not contain an INTO clause, as we are not extracting data into host variables at this stage. The next step is to open the cursor, to make it ready for fetching the results: EXEC SQL open mycursor; Now we can start to retrieve the result rows. We do this by executing a FETCH with an INTO clause to extract the data values: EXEC SQL fetch next from mycursor into :var1, :var2, ; When there are no more result rows left to fetch, we will get a row count in sqlca.sqlerrd[2] of zero and an sqlca.sqlcode of 100. When we have finished with the cursor, we close it and end the transaction: EXEC SQL close mycursor; EXEC SQL COMMIT; The following is a sample program (cursor.pgc) that uses a cursor to retrieve the results, similar to a query we saw in Chapter 7. It extracts the dates on which the orders were placed by customers living in a specified town. #include <stdio.h> #include <string.h> #include <stdlib.h> MatthewStones_4789.book Page 441 Wednesday, February 23, 2005 6:49 AM 442 CHAPTER 14 ■ ACCESSING POSTGRESQL FROM C USING EMBEDDED SQL EXEC SQL include sqlca; EXEC SQL whenever sqlwarning sqlprint; EXEC SQL whenever sqlerror do GiveUp(); void GiveUp() { fprintf(stderr, "Fatal Error\n"); sqlprint(); exit(1); } main(int argc, char *argv[]) { EXEC SQL BEGIN declare section; varchar town[64]; int town_ind; double shipping; char date[10]; EXEC SQL END declare section; if(argc != 2) { printf("Usage: cursor town\n"); exit(1); } town.len = strlen(argv[1]); strncpy(town.arr, argv[1], town.len); town.arr[town.len] = '\0'; // ECPGdebug(1, stderr); EXEC SQL CONNECT TO bpfinal; EXEC SQL declare mycursor cursor for SELECT oi.date_placed, oi.shipping FROM customer c, orderinfo oi WHERE c.customer_id = oi.customer_id and c.town = :town; EXEC SQL open mycursor; EXEC SQL whenever sqlwarning continue; EXEC SQL whenever sqlerror continue; while(sqlca.sqlcode == 0) { MatthewStones_4789.book Page 442 Wednesday, February 23, 2005 6:49 AM CHAPTER 14 ■ ACCESSING POSTGRESQL FROM C USING EMBEDDED SQL 443 EXEC SQL fetch from mycursor into :date, :shipping; if (sqlca.sqlcode == 0) { printf("%.*s <%.2f>\n", sizeof(date), date, shipping); } } EXEC SQL whenever sqlwarning sqlprint; EXEC SQL whenever sqlerror do GiveUp(); EXEC SQL close mycursor; EXEC SQL DISCONNECT ALL; } This program now neatly takes care of the three cases we might have: finding no orders, finding exactly one order, and finding many orders. $ make cursor $ ./cursor Erewhon $ ./cursor Milltown 2000-09-02 <3.99> $ ./cursor Bingham 2000-06-23 <0.00> 2000-07-21 <0.00> Notice we mix the use of EXEC SQL whenever and more conventional error checking, with the sqlca.sqlcode used to control the loop while data is successfully being retrieved from the database. To ensure the code behaves as we expect, we must reset the warning and error handing (EXEC SQL whenever sqlwarning continue and EXEC SQL whenever sqlerror continue) before we get to the code where we wish to check sqlca.sqlcode. Once we complete the section of code where we wish to handle errors by checking the sqlca.sqlcode explicitly, we return to our previous error-handling behavior. Debugging ecpg Code Although ecpg does a good job of generating C code from pgc files, occasionally you will have a problem compiling the code. This is usually because of a mistake in your C code rather than anything ecpg has done, and you may want to look at the generated code from ecpg, using the real line numbers in the generated C code. To do this, you need to employ a little trick to remove the #line preprocessor directives ecpg inserts, which generally force compiler errors to refer to the original .pgc file, not the .c file that is actually being compiled. This involves the following steps: • Manually run ecpg to generate a .c file from the .pgc file. •Use grep to remove the #line directives, writing a new temporary file. • Move the temporary file back to its rightful place. • Allow compilation to continue, or invoke the C compiler manually. MatthewStones_4789.book Page 443 Wednesday, February 23, 2005 6:49 AM 444 CHAPTER 14 ■ ACCESSING POSTGRESQL FROM C USING EMBEDDED SQL Here is an example of how we might do this with cursor.pgc: $ ecpg -I/usr/local/pgsql/include cursor.pgc $ grep -v '^#line' cursor.c > _1.c At this point, we have a C file, _1.c, which contains the preprocessed version of cursor.pgc, but with all the compiler line control settings stripped out. We move this back to the original filename, cursor.c, and then call make, to allow it to perform the final step in the compilation process of generating an executable. $ mv _1.c cursor.c $ make cc -I/usr/local/pgsql/include -L/usr/local/pgsql/lib -lecpg -lpq cursor.c -o cursor $ ./cursor Milltown 2000-09-02 <3.99> $ When we run the code, we see exactly the same output as before. However, if we did get an error, the line numbers from cursor.c would be displayed, not those from cursor.pgc. Summary This chapter explained how to use SQL in C programs by embedding SQL statements directly in the source code. The translator ecpg then generates C code that the compiler can understand to produce an executable. We covered how to connect to a database and deal with errors that may occur. We demon- strated how to use host variables to provide values for INSERT and UPDATE statements. Next, we saw how to implement simple SELECT statements and extract row data into host variables, and then use host variables to specify part of the WHERE clause. We also saw how to use indicator variables to detect null values in the data being retrieved. We then explored how to use a cursor to retrieve multiple rows returned as a result of a more complex query. In this chapter, we have built on what we learned in the previous chapter and used a more portable way of interfacing PostgreSQL to C. In some ways, the libpq method allows slightly more control over result sets and status information. It also allows an asynchronous mode of operation. On the other hand, embedding SQL makes it easier to deal with binary values (as ecpg takes care of all of the conversions needed), is more portable, and generally makes it much easier to read the underlying SQL in the program. The method you choose depends on the requirements of your application. In the next chapter, we move onto another programming language we can use with PostgreSQL: PHP. MatthewStones_4789.book Page 444 Wednesday, February 23, 2005 6:49 AM 445 ■ ■ ■ CHAPTER 15 Accessing PostgreSQL from PHP With the proliferation of web-based interfaces for everything from online banking to univer- sity course scheduling systems to online dating services, integrating database-driven back-ends with browser-based front-ends has become an incredibly important (and sensitive) aspect of online development. Web-based interfaces have achieved enormous success for the following reasons: • Web browsers offer a common and familiar interface for browsing data. • Web-based applications can easily be integrated into an existing web site. • Web (HTML) interfaces are easily created and modified. In this chapter, we will explore various methods for accessing PostgreSQL from PHP. PHP is a server-side, cross-platform scripting language for writing web-based applications. It allows programmers to embed program logic in HTML pages, and thus serve dynamic web pages. PHP allows us to create web-based user interfaces that interact with PostgreSQL. Here, we will focus on designing PHP scripts that make effective use of PHP’s PostgreSQL interface. ■Note In this chapter, we will assume at least a basic understanding of the PHP language and the use of PHP version 4 or 5 (most of the code examples and descriptions also apply to earlier versions of PHP, but there may be a few differences in functionality). If you are unfamiliar with PHP, you might want to explore the PHP web site, at http://www.php.net/. You can also refer to Beginning PHP 5 and MySQL: From Novice to Professional, by Jason Gilmore (Apress, 2004; ISBN 1-893115-51-8). Adding PostgreSQL Support to PHP Before you can begin developing PHP scripts that interface with a PostgreSQL database, you will need to include PostgreSQL support in your PHP installation. If you are unsure whether your existing PHP installation already has PostgreSQL support, create a simple script named phpinfo.php (which should be placed in your web server’s docu- ment root), containing the following lines: MatthewStones_4789C15.fm Page 445 Friday, February 25, 2005 5:20 PM 446 CHAPTER 15 ■ ACCESSING POSTGRESQL FROM PHP <?php phpinfo(); ?> Examine the output of this script in your web browser. If PostgreSQL support is available, the browser output will contain a section similar to that shown in Figure 15-1. Figure 15-1. Checking for PostgreSQL support in a PHP installation Alternatively, if you’re running PHP version 4.3.0 or later, you can run php –m from the command line and look for pgsql in the list of modules. If your PHP installation already has PostgreSQL support, you can skip to the next section in this chapter. If you need to add PostgreSQL support and you are building PHP from source, this is fairly easy. Simply pass the with-pgsql option to the configure script: $ ./configure with-pgsql You can optionally specify the directory of your PostgreSQL installation if the configure script is unable to locate it by itself: $ ./configure with-pgsql=/var/lib/pgsql Remember that you might need to pass additional options to the configure script depending on your build requirements. For example, to build PHP with support for PostgreSQL, IMAP, and LDAP, you would use the following command line: $ ./configure with-pgsql with-imap with-ldap Refer to the PHP documentation (specifically, the INSTALL document included with the PHP distribution) for additional compilation options and installation instructions. You can also read these instructions online at http://www.php.net/manual/en/install.php. Using the PHP API for PostgreSQL All of the interaction with the PostgreSQL database is performed through the PostgreSQL extension, which provides a comprehensive set of PHP functions. For a complete list of functions and further information about them, refer to http://www.php.net/pgsql. MatthewStones_4789C15.fm Page 446 Friday, February 25, 2005 5:20 PM CHAPTER 15 ■ ACCESSING POSTGRESQL FROM PHP 447 A simple PHP script follows. It opens a connection to a PostgreSQL database named bpsimple, selects some rows, prints the number of rows in the result set, frees the memory consumed by the rows, and then closes the connection. <?php /* Connect to the PostgreSQL database and store the connection handle. */ $db_handle = pg_connect('dbname=bpsimple user=jon password=secret'); /* Define and execute our SQL query string. */ $query = 'SELECT * FROM item'; $result = pg_query($db_handle, $query); /* Print the number of rows in the result using pg_num_rows(). */ echo 'Number of rows: ' . pg_num_rows($result); /* Free the memory used by the result set and close the connection handle. */ pg_free_result($result); pg_close($db_handle); ?> As you can see, interacting with the database from within PHP is fairly straightforward. We will now cover the various aspects of the PHP PostgreSQL extension in more depth. Making Database Connections Before interacting with the database, a connection must first be opened. Each connection is represented by a single variable. We’ll refer to this variable as the connection handle. PHP allows you to have multiple connections open at once, each with its own unique connection handle. This is useful in the case where a single PHP script needs to communicate with multiple database servers. Creating a New Database Connection We open a database connection using the pg_connect() function. This function takes a connection string as its only argument and returns a database connection handle. Here’s an example: $db_handle = pg_connect('dbname=bpsimple user=jon password=secret'); In this example, the connection string specifies the database name (dbname=bpsimple), a username (user=jon), and the user’s password (password=secret). You have the option of using single quotes to delimit the connection strings, as used in the previous example, but if you want to use PHP variables, remember to surround the connection string in double quotes: $dbname = 'bpsimple'; $user = 'jon'; $password = 'secret'; $db_handle = pg_connect("dbname=$dbname user=$user password=$password"); MatthewStones_4789C15.fm Page 447 Friday, February 25, 2005 5:20 PM 448 CHAPTER 15 ■ ACCESSING POSTGRESQL FROM PHP All of the standard PostgreSQL connection parameters are available in the connection string. The most commonly used parameters and their meanings are shown in Table 15-1. If the connection attempt fails, the pg_connect() function will return false. Failed connection attempts can thus be detected by testing the return value: <?php $db_handle = pg_connect('dbname=bpsimple user=jon password=secret'); if ($db_handle) { echo 'Connection attempt succeeded.'; } else { echo 'Connection attempt failed.'; } pg_close($db_handle); ?> As we mentioned, PHP supports multiple concurrent database connections. Each call to pg_connect() will return a new connection handle. Each connection attempt specifies its own connection string: $db_handle1 = pg_connect('dbname=billing user=dan'); $db_handle2 = pg_connect('dbname=inventory user=tom'); Creating a Persistent Connection PHP also supports persistent database connections. Persistent connections are held open beyond the lifetime of the page request, whereas normal connections are closed at the end of the page request. PHP maintains a list of currently open connections and, if a request is made for a new persistent database connection with the same connection parameters as one of the open connections in this list, a handle to the existing opened connection is returned instead. This has the advantage of saving the script the additional overhead of creating a new database connection when a suitable one already exists in the connection pool. To open a persistent connection to PostgreSQL, use the pg_pconnect() function. This function behaves exactly like the pg_connect() function described in the previous section, except that it requests a persistent connection, if one is available. Table 15-1. Common Connection Parameters Parameter Meaning dbname The name of the database to which we want to connect user The username to use when connecting to the target database password The user’s password, which authenticates the user’s access to the database host The host name of the server on which PostgreSQL is running hostaddr The IP address of the server on which PostgreSQL is running port The TCP/IP port on which the PostgreSQL server is listening MatthewStones_4789C15.fm Page 448 Friday, February 25, 2005 5:20 PM [...]... a PostgreSQL-specific interface, such as libpq from C A Perl program using DBI generally takes the following form: #!/usr/bin/perl –w use DBI; use strict; # Connect to a PostgreSQL database # Prepare reusable SQL statements LOOP: # Execute a SQL statement # Read query results # Disconnect from database MatthewStones_4 789 C16.fm Page 473 Friday, March 4, 2005 6:44 PM CHAPTER 16 ■ ACCESSING POSTGRESQL... 5:20 PM MatthewStones_4 789 C16.fm Page 465 Friday, March 4, 2005 6:44 PM CHAPTER 16 ■■■ Accessing PostgreSQL from Perl A s earlier chapters have shown, communicating with PostgreSQL generally involves a lot of string manipulation One language that excels at string manipulation is Perl In Chapter 13, we demonstrated that the libpq interface is a powerful way to access a PostgreSQL database, but there... 16-1 Perl DBI architecture MatthewStones_4 789 C16.fm Page 469 Friday, March 4, 2005 6:44 PM CHAPTER 16 ■ ACCESSING POSTGRESQL FROM PERL There are many DBD modules that provide DBI access to different data sources, including PostgreSQL and Oracle, as shown in Figure 16-1 In this chapter, we will be using DBD::PgPP, a Pure Perl module for accessing PostgreSQL databases specifically, and DBD::ODBC, a module... query failed with the following error:\n"; echo pg_last_error($db_handle); } pg_close($db_handle); ?> Getting Field Information PostgreSQL supports a notion of NULL field values PHP doesn’t necessarily define NULL the same way PostgreSQL does, however To account for this, PHP provides the MatthewStones_4 789 C15.fm Page 457 Friday, February 25, 2005 5:20 PM CHAPTER 15 ■ ACCESSING POSTGRESQL FROM PHP... host name associated with the current connection pg_options() Returns the options associated with the current connection (except for the database name) pg_port() Returns the port number of the current connection pg_tty() Returns the TTY name associated with the current connection 449 MatthewStones_4 789 C15.fm Page 450 Friday, February 25, 2005 5:20 PM 450 CHAPTER 15 ■ ACCESSING POSTGRESQL FROM PHP All of... C:\Perl\bin\dbiproxy Installing C:\Perl\bin\dbiproxy.bat Successfully installed DBI version 1.45 in ActivePerl 5 .8. 4 .81 0 ppm> Next, we install the Pure Perl driver for PostgreSQL, DBD::PgPP: ppm> install DBD::PgPP ==================== Install 'DBD-PgPP' version 0.04 in ActivePerl 5 .8. 4 .81 0 ==================== Downloaded 10205 bytes Extracting 5/5: blib/arch/auto/DBD/PgPP/.exists Installing C:\Perl\html\site\lib\DBD\PgPP.html... ppm> install DBI ==================== Install 'DBI' version 1.45 in ActivePerl 5 .8. 4 .81 0 ==================== Installing the Perl DBI If you have programmed databases in Windows, you will be familiar with the Open Database Connectivity (ODBC) API or more recent APIs, such as ADO or OLE DB Similarly, if you have used Java with databases, you will have come across JDBC These programming interfaces are an... want to consider releasing query resources as possible One function, pg_free_result(), is available for this task pg_free_result($result); 457 MatthewStones_4 789 C15.fm Page 4 58 Friday, February 25, 2005 5:20 PM 4 58 CHAPTER 15 ■ ACCESSING POSTGRESQL FROM PHP Using this function is necessary only if you’re especially worried about memory consumption in your script, and you know you won’t be using this... interfaces to databases definitely adds benefits If you know even a little about Perl, you will be aware that one of the language’s axioms is that there is always more than one way to tackle any given job In fact, Perl enthusiasts would be disappointed if they had to limit their options to single figures We do not propose to bombard you with numerous techniques for accessing PostgreSQL databases from... shortcut similar to the CPAN module in the ppm command You can use ppm to download and install precompiled modules for Perl on Microsoft Windows 467 MatthewStones_4 789 C16.fm Page 4 68 Friday, March 4, 2005 6:44 PM 4 68 CHAPTER 16 ■ ACCESSING POSTGRESQL FROM PERL C:\Documents and Settings\Neil>ppm PPM - Programmer's Package Manager version 3.1 Copyright (c) 2001 ActiveState Corp All Rights Reserved ActiveState . <stdlib.h> MatthewStones_4 789 .book Page 441 Wednesday, February 23, 2005 6:49 AM 442 CHAPTER 14 ■ ACCESSING POSTGRESQL FROM C USING EMBEDDED SQL EXEC SQL include sqlca; EXEC SQL whenever sqlwarning sqlprint; EXEC SQL. 2004; ISBN 1 -89 3115-51 -8) . Adding PostgreSQL Support to PHP Before you can begin developing PHP scripts that interface with a PostgreSQL database, you will need to include PostgreSQL support. and c.town = :town; EXEC SQL open mycursor; EXEC SQL whenever sqlwarning continue; EXEC SQL whenever sqlerror continue; while(sqlca.sqlcode == 0) { MatthewStones_4 789 .book Page 442 Wednesday,