PHPandPostgreSQLAdvancedWebProgrammingChapter11WritingDatabaseD PDF

You might also like

You are on page 1of 18

Table of Contents

Chapter 11. Writing Database-Driven Applications.................................................................. 1


Section 11.1. Connecting to the Database........................................................................................................................................................................................ 1
Section 11.2. Inserting and Retrieving Data.................................................................................................................................................................................... 3
Section 11.3. Error Handling and Monitoring................................................................................................................................................................................ 8
Section 11.4. Handling Huge Amounts of Data............................................................................................................................................................................. 10
Section 11.5. Retrieving Objects from the Database...................................................................................................................................................................... 12
Section 11.6. Tracing a PostgreSQL Connection........................................................................................................................................................................... 13
Section 11.7. Locking...................................................................................................................................................................................................................... 14
Section 11.8. PHP and Transactions.............................................................................................................................................................................................. 15
Section 11.9. Summary................................................................................................................................................................................................................... 16

Chapter 11. Writing Database-Driven Applications. PHP and PostgreSQL: Advanced Web Programming, ISBN: 0-672-32382-6
Prepared for punfzr@hub2.nic.in, Dinesh Sharma
Copyright 2002 by Sams Publishing. This download file is made available for personal use only and is subject to the Safari Terms of Service. Any other use requires prior written consent
from the copyright owner. Unauthorized use, reproduction and/or distribution are strictly prohibited and violate applicable laws. All rights reserved.
262

Chapter 11. Writing Database-Driven


Applications

After 10 hard chapters about either PHP or PostgreSQL, it's time to write the first applications based
on PHP and PostgreSQL as a team. The goal of this chapter is to introduce you to the basics of
PHP/PostgreSQL interaction and to learn how to send SQL statements to the database server. In
addition, you will learn what you have to take care of when working with PostgreSQL and PHP.

Connecting to the Database


The first thing you have to do when working with PHP and PostgreSQL is to connect to the database.
Just as when you're working with a file, you need a handle to identify a connection and to interact
with it. To generate this handle, you must use pg_connect. pg_connect establishesa connection
to the database and returns a database handle. The connect string sent to the database is the same
string you'd use in a C program. This is an important point because PHP's PostgreSQL interface is
built on C, so the behavior of PHP is easy to understand if you are familiar with the C interface.
However, to work with this book, you don't have to be familiar with C.
Let's see how to establish a connection to the database:
<?php
$connstr = "dbname=phpbook user=postgres
host=localhost port=5432";
$dbh = pg_connect($connstr);

if ($dbh)
{
echo "the connection to the database has been established<br>";
}
else
{
echo "no connection has been established<br>";
}
?>

First, a connect string is defined that contains the name of the database you want to connect to,
and the host the database can be found on. In addition, the port has been specified. This string is
passed to pg_connect and a database handle is returned. If the database handle is returned
correctly, a message is displayed.
Several things can cause PHP to return a wrong database handle:
PHP or PostgreSQL have been installed the wrong way.
You have defined a wrong database, name, host, user, port, or password.
PostgreSQL has run out of connections because too many users are connected to the database.
The database cannot be reached via the network. Maybe you have not started the postmaster
using -i.

Chapter 11. Writing Database-Driven Applications. PHP and PostgreSQL: Advanced Web Programming, ISBN: 0-672-32382-6
Prepared for punfzr@hub2.nic.in, Dinesh Sharma
Copyright 2002 by Sams Publishing. This download file is made available for personal use only and is subject to the Safari Terms of Service. Any other use requires prior written consent
from the copyright owner. Unauthorized use, reproduction and/or distribution are strictly prohibited and violate applicable laws. All rights reserved.
Writing Database-Driven Applications 263

If you have configured PHP and PostgreSQL properly, and if the database exists on the system, it
should not be a problem to connect to the database.
The syntax we have just discussed is the new syntax of pg_connect. The old system is still avail-
able but should not be used any more:
int pg_connect (string host, string port, string options, string tty, string
dbname)

Now that you have seen how to open a database connection, it is time to see how to close a con-
nection at the end of a script:
<?php
$connstr = "dbname=phpbook user=postgres host=localhost port=5432";
$dbh = @pg_connect($connstr);

if ($dbh)
{

Licensed by
echo "the connection to the database has been established<br>";
}
$status = pg_close ($dbh);
if ($status == TRUE)
{
echo "Connection terminated successfully<br>";

Dinesh Sharma
echo "Status: $status<br>";
}
?>

First, a connection to the database is established and the script checks whether everything has
been done correctly. If an error occurs, the error will be suppressed by the @ symbol. This can be

971061
very important because in real-world applications it can be fatal if a user can see error messages
on the screen. On the one hand, it does not look professional if a site displays errors, and on the
other hand, it can be a potential security threat if certain information is displayed.
Now that you have seen how to connect to the database, take a closer look at the connect string
what happens if not all components of the string are defined? For every parameter that is not defined,
the default value of that parameter will be used. Here's an example:
<?php
$connstr = "dbname=phpbook user=postgres";
$dbh = pg_connect($connstr);
if ($dbh) {echo "connection to phpbook established ...<br>";}

$connstr = "user=postgres";
$dbh = pg_connect($connstr);
if ($dbh) {echo "connection to postgres established ...<br>";}
?>

The first connect string does not contain a hostname. This will make PHP connect to PostgreSQL
to the local using Unix sockets. In this case no TCP/IP will be used. In addition, the default port is
not necessary because Unix sockets don't need a port. Because the database and the user exist,
the connection can be established successfully. The next connect string does not contain the name
of the database you want to connect to. In this case PostgreSQL will assume that the name of the
database is the same as the userin this example, PostgreSQL assumes that the name of the
database is postgres as well. When the script executes, an error will be displayed:
connection to phpbook established ...

Warning: Unable to connect to PostgreSQL server: FATAL 1: Database "postgres"


does not exist in the system catalog. in /var/www/html/connect.php on line 7

There is no database called postgres on the system, so an error is displayed by the PHP inter-
preter.

Chapter 11. Writing Database-Driven Applications. PHP and PostgreSQL: Advanced Web Programming, ISBN: 0-672-32382-6
Prepared for punfzr@hub2.nic.in, Dinesh Sharma
Copyright 2002 by Sams Publishing. This download file is made available for personal use only and is subject to the Safari Terms of Service. Any other use requires prior written consent
from the copyright owner. Unauthorized use, reproduction and/or distribution are strictly prohibited and violate applicable laws. All rights reserved.
Writing Database-Driven Applications 264

After a connection has been established, it might be necessary to find out which parameters have
been used in order to establish the connection to PostgreSQL. Therefore PHP provides a set of
functions for retrieving that information:
<?php
$connstr = "dbname=phpbook user=postgres host=localhost";
$dbh = @pg_connect($connstr);

if ($dbh)
{

echo "Handle exists<br>";


}

echo "Host: ".pg_host($dbh)."<br>";


echo "Database: ".pg_dbname($dbh)."<br>";
echo "Port: ".pg_port($dbh)."<br>";
echo "tty: ".pg_tty($dbh)."<br>";
?>

pg_host returns the host you are connected to. pg_dbname returns the name of the data,
pg_port returns the number of the port PostgreSQL is listening to, and pg_tty returns the current
tty. If you execute the script in the preceding listing, the result could look like this:
Handle exists
Host: localhost
Database: phpbook
Port: 5432
tty:

We recommend using these functions only if you are connected to PostgreSQL via TCP/IP. In the
case of Unix sockets, it seems as if PHP is not that stable yet. In version 4.06, PHP core dumps
when calling pg_host if the connection has been established via Unix sockets.
After establishing the connection, it is closed explicitly by using pg_close. Closing database han-
dles is not necessary because the backend process associated with a connection is destroyed
automatically at the end of a PHP script. However, in applications that are implemented properly,
database handles are closed explicitly. In this scenario you have seen how to work with nonpersis-
tent connections. How to use persistent connections will be covered in Chapter 13, Working with
Persistent Database Connections.
Let's execute the script and see what is displayed on the screen:
the connection to the database has been established
Connection terminated successfully
Status: 1

The connection has been started and terminated successfully.

Inserting and Retrieving Data


Connecting to the database is just not enough. In this section you will learn how to communicate
with the database, how to create tables, and how to insert and retrieve data.
The first thing to do is to create a new table. Therefore you can write a short PHP script:
<?php
$connstr = "dbname=phpbook user=postgres host=localhost";
$dbh = pg_connect($connstr);
if ($dbh) {echo "connection to phpbook established ...<br>";}

$sql = "CREATE TABLE person (id serial, name text, location text)";
$stat = pg_exec($dbh, $sql);

Chapter 11. Writing Database-Driven Applications. PHP and PostgreSQL: Advanced Web Programming, ISBN: 0-672-32382-6
Prepared for punfzr@hub2.nic.in, Dinesh Sharma
Copyright 2002 by Sams Publishing. This download file is made available for personal use only and is subject to the Safari Terms of Service. Any other use requires prior written consent
from the copyright owner. Unauthorized use, reproduction and/or distribution are strictly prohibited and violate applicable laws. All rights reserved.
Writing Database-Driven Applications 265

if ($stat)
{
echo "The table has successfully been created<br>\n";
}
else
{
echo "creating the table failed<br>\n";
}
pg_close($dbh);
?>

After connecting to the database, a string containing the SQL code is generated. In the next step
the query is sent to the server. pg_exec returns a status code that can be checked. Two parameters
have to be passed to pg_exec: The first parameter is the connection handle and the second pa-
rameter contains the SQL statement you want to execute. If the statement has been executed suc-
cessfully, the following text will be displayed:
connection to phpbook established ...
The table has successfully been created

To see if the table is really in the database, you can use psql and \d:
phpbook=# \d person
Table "person"
Attribute | Type | Modifier
-----------+---------+---------------------------------------------------
id | integer | not null default nextval('"person_id_seq"'::text)
name | text |
location | text |
Index: person_id_key

Now that you have created a table, you can start inserting records. The next example shows how
one record can be added to the table:
<?php
$connstr = "dbname=phpbook user=postgres host=localhost";
$dbh = pg_connect($connstr);
if ($dbh) {echo "connection to phpbook established ...<br>";}

$sql = "INSERT INTO person (name, location) VALUES ('Paul', 'Vienna')";


$stat = pg_exec($dbh, $sql);
if ($stat)
{
echo "The record has been inserted successfully<br>\n";
}
else
{
echo "creating the table failed<br>\n";
}
pg_close($dbh);
?>

As you can see, adding records to the table works the same way as adding tables or executing any
other SQL statement. In this regard, PHP is flexible and easy to use.
The text displayed by PHP is not surprising:
connection to phpbook established ...
The record has been inserted successfully

Now that the table contains some data, you can retrieve some records. Retrieving data is slightly
more complex than inserting data or making other changes. To retrieve data, you must execute the
query first. In the next step, the number of records in the result can be computed. Finally, the data
can be retrieved using a simple loop as shown in the next listing:

Chapter 11. Writing Database-Driven Applications. PHP and PostgreSQL: Advanced Web Programming, ISBN: 0-672-32382-6
Prepared for punfzr@hub2.nic.in, Dinesh Sharma
Copyright 2002 by Sams Publishing. This download file is made available for personal use only and is subject to the Safari Terms of Service. Any other use requires prior written consent
from the copyright owner. Unauthorized use, reproduction and/or distribution are strictly prohibited and violate applicable laws. All rights reserved.
Writing Database-Driven Applications 266

<?php
$connstr = "dbname=phpbook user=postgres host=localhost";
$dbh = pg_connect($connstr);
if ($dbh) {echo "connection to phpbook established ...<br>";}

$sql = "SELECT id, name, location FROM person";


$stat = pg_exec($dbh, $sql);
if ($stat)
{
$rows = pg_numrows($stat);
echo "$rows lines returned by PostgreSQL<br>\n";
}
else
{
echo "an error has occurred while processing ($sql)<br>\n";
}
pg_close($dbh);
?>

For computing the number of lines returned by a query, you can use pg_numrows. In the scenario
shown in the preceding listing, the number of lines is displayed on the screen:
connection to phpbook established ...
1 lines returned by PostgreSQL

In the next example you can see how the lines returned by a query can be retrieved and displayed:
<?php
$connstr = "dbname=phpbook user=postgres host=localhost";
$dbh = pg_connect($connstr);
if ($dbh) {echo "connection to phpbook established ...<br>";}

$stat = pg_exec($dbh, "SELECT id, name, location FROM person");


$rows = pg_numrows($stat);

for ($i = 0; $i < $rows; $i++)


{
$data = pg_fetch_row($stat, $i);
echo "data: $data[0], $data[1], $data[2]<br>\n";
}
pg_close($dbh);
?>

After computing the number of lines, a loop goes through all records and retrieves the data in the
result. To extract one line of data, the command pg_fetch_row has to be used. The command
returns an array that can easily be processed and displayed on the screen using echo:
connection to phpbook established ...
data: 1, Paul, Vienna

In this example, a fixed number of columns is used and displayed on the screen. In many cases,
however, a dynamic number of columns has to be displayed. Therefore the number of columns has
to be computed at runtime. The next example shows how this can be done and how data can be
displayed:
<?php
$connstr = "dbname=phpbook user=postgres host=localhost";
$dbh = pg_connect($connstr);
if ($dbh) {echo "connection to phpbook established ...<br>";}

$stat = pg_exec($dbh, "SELECT id, name, location FROM person");


$rows = pg_numrows($stat);
$cols = pg_numfields($stat);
echo "rows: $rows; columns: $cols<br>\n";
for ($i = 0; $i < $rows; $i++)
{

Chapter 11. Writing Database-Driven Applications. PHP and PostgreSQL: Advanced Web Programming, ISBN: 0-672-32382-6
Prepared for punfzr@hub2.nic.in, Dinesh Sharma
Copyright 2002 by Sams Publishing. This download file is made available for personal use only and is subject to the Safari Terms of Service. Any other use requires prior written consent
from the copyright owner. Unauthorized use, reproduction and/or distribution are strictly prohibited and violate applicable laws. All rights reserved.
Writing Database-Driven Applications 267

$data = pg_fetch_row($stat, $i);


for ($j = 0; $j < $cols; $j++)
{
echo "$data[$j] ";
}
echo "<br>\n";
}
pg_close($dbh);
?>

pg_numfields is used to retrieve the number of columns in the result. Just like the result of
pg_numrows, it is used by a loop. The output of the script is not surprising:
connection to phpbook established ...
rows: 1; columns: 3
1 Paul Vienna

One line of data is displayed.


In the past few examples, data has been retrieved using pg_fetch_row. The next example shows
how the same result can be achieved by using pg_fetch_array:
<?php
$connstr = "dbname=phpbook user=postgres host=localhost";
$dbh = pg_connect($connstr);
if ($dbh) {echo "connection to phpbook established ...<br>";}

$stat = pg_exec($dbh, "SELECT id, name, location FROM person");


$rows = pg_numrows($stat);
echo "rows: $rows<br>\n";
for ($i = 0; $i < $rows; $i++)
{
$data = pg_fetch_array($stat, $i);
echo $data["id"]." ".$data["name"]." ".$data["location"];
echo "<br>\n";
}
pg_close($dbh);
?>

This time the data can be accessed by using the name of the column. As you can easily imagine,
this is far better than working with indexes because the columns in the result cannot be mixed up.
The output of the script is similar to the one you have already seen:
connection to phpbook established ...
rows: 1
1 Paul Vienna

An important point when working with pg_fetch_array is to see how you can work with aliases:
<?php
$connstr = "dbname=phpbook user=postgres host=localhost";
$dbh = pg_connect($connstr);
if ($dbh) {echo "connection to phpbook established ...<br>";}

$stat = pg_exec($dbh, "SELECT cos(id) AS a FROM person");


$rows = pg_numrows($stat);
echo "rows: $rows<br>\n";
for ($i = 0; $i < $rows; $i++)
{
$data = pg_fetch_array($stat, $i);
echo "data: ".$data["a"]."<br>\n";
}
pg_close($dbh);
?>

Chapter 11. Writing Database-Driven Applications. PHP and PostgreSQL: Advanced Web Programming, ISBN: 0-672-32382-6
Prepared for punfzr@hub2.nic.in, Dinesh Sharma
Copyright 2002 by Sams Publishing. This download file is made available for personal use only and is subject to the Safari Terms of Service. Any other use requires prior written consent
from the copyright owner. Unauthorized use, reproduction and/or distribution are strictly prohibited and violate applicable laws. All rights reserved.
Writing Database-Driven Applications 268

An alias is used for the field called cos, which contains the cosine of the id. To extract the data from
the result, you have to access the alias in order to find the correct result. As you can see in the next
listing, it works perfectly:
connection to phpbook established ...
rows: 1
data: 0.54030230586814

In many cases, you need to retrieve information about the result itself. You have already seen how
to compute the number of rows returned by a query. In the next example you will see how to obtain
some additional information:
<?php
$connstr = "dbname=phpbook user=postgres host=localhost";
$dbh = pg_connect($connstr);
if ($dbh) {echo "connection to phpbook established ...<br><br>";}

$stat = pg_exec($dbh, "SELECT id, name, location FROM person");

$myid = pg_fieldname($stat, 0);


echo "field number 1 is called $myid<br>\n";

$myname = pg_fieldnum($stat, "name") + 1;


echo "'name' is field number $myname<br>";

$myloc = pg_fieldsize($stat, 2);


echo "the third field is $myloc bytes long<br>";

pg_close($dbh);
?>

To find the name of a column, you can use pg_fieldname. Just pass the name of the result and
the index of the column to the function, and PHP and PostgreSQL will do the rest for you. To find
the id of a certain column, PHP offers the pg_fieldnum function. This time the name of the result
and the name of the column have to be passed to the function.
The last function discussed in this example is pg_fieldsize, which returns the length of a column.
In the case of a variable length, the function will return -1 because the length cannot be computed
precisely.
The next listing shows the output when the script is executed:
connection to phpbook established ...

field number 1 is called id


'name' is field number 2
the third field is -1 bytes long

Complex operations with a lot of data involved might use a lot of memory. If many memory-con-
suming requests have to be processed simultaneously, your Web server might run out of memory
and the performance of your system could decrease significantly because of swapping and ineffi-
cient behavior. To reduce the amount of memory used during complex operations, PHP provides a
command called pg_freeresult. If the result of a query is not needed any more, this command
will help you to free the memory allocated by PHP to store the result returned by PostgreSQL.
Normally the memory allocated by PHP is freed at the end of the script automatically. pg_free-
result can be used to free it earlier in the process. Let's take a look at an example:
<?php
$connstr = "dbname=phpbook user=postgres host=localhost";
$dbh = pg_connect($connstr);
if ($dbh) {echo "connection to phpbook established ...<br>";}

$result = pg_exec($dbh, "SELECT cos(id) AS a FROM person");

Chapter 11. Writing Database-Driven Applications. PHP and PostgreSQL: Advanced Web Programming, ISBN: 0-672-32382-6
Prepared for punfzr@hub2.nic.in, Dinesh Sharma
Copyright 2002 by Sams Publishing. This download file is made available for personal use only and is subject to the Safari Terms of Service. Any other use requires prior written consent
from the copyright owner. Unauthorized use, reproduction and/or distribution are strictly prohibited and violate applicable laws. All rights reserved.
Writing Database-Driven Applications 269

for ($i = 0; $i < pg_numrows($result); $i++)


{
$data = pg_fetch_array($result, $i);
echo "data: ".$data["a"]."<br>\n";
}

$stat = pg_freeresult($result);
if ($stat)
{
echo "memory freed successfully<br>\n";
}
pg_close($dbh);
?>

In this example the memory is freed manually and the script checks whether the command has been
successful:
connection to phpbook established ...
data: 0.54030230586814
memory freed successfully

No errors were displayed and the script terminated without any problems.
In many cases, the functions you have just seen are essential, and they are the basis of many
sophisticated applications.

Error Handling and Monitoring


Error handling is one of the most important thingsnot only when working with databases. PHP
and PostgreSQL have error-handling mechanisms that are easy to use and reliable.
Let's create a table for storing telephone numbers:
phpbook=# CREATE TABLE phone (id serial, name text, phone text NOT NULL);
NOTICE: CREATE TABLE will create implicit sequence 'phone_id_seq' for SERIAL
column 'phone.id'
NOTICE: CREATE TABLE/UNIQUE will create implicit index 'phone_id_key' for
table 'phone'
CREATE

The last column has been defined as NOT NULL, which means that a value has to be added to the
column. In the next step you can try to write a script that tries to insert some data into the table:
<?php
$connstr = "dbname=phpbook user=postgres host=localhost";
$dbh = pg_connect($connstr);
if ($dbh) {echo "connection to phpbook established ...<br>";}

$stat = pg_exec($dbh, "INSERT INTO phone (name) VALUES ('Susan')");


if ($stat)
{
echo "data inserted successfully<br>\n";
}
else
{
echo "<br>an error has occurred<br>\n";
echo pg_errormessage($dbh)."<br>";
}
pg_close($dbh);
?>

Chapter 11. Writing Database-Driven Applications. PHP and PostgreSQL: Advanced Web Programming, ISBN: 0-672-32382-6
Prepared for punfzr@hub2.nic.in, Dinesh Sharma
Copyright 2002 by Sams Publishing. This download file is made available for personal use only and is subject to the Safari Terms of Service. Any other use requires prior written consent
from the copyright owner. Unauthorized use, reproduction and/or distribution are strictly prohibited and violate applicable laws. All rights reserved.
Writing Database-Driven Applications 270

After connecting to the database, an INSERT statement is executed. Because no telephone number
has been passed to the database, an error will occur that can be displayed on the screen using
pg_errormessage. The database handle, not the return value of the execute statement, has to
be passed to the functionthis is an important point.
Let's see what the output is when the script is executed:
connection to phpbook established ...

Warning: PostgreSQL query failed: ERROR: ExecAppend: Fail to add null value in
not null attribute phone in /var/www/html/createtab.php on line 6

an error has occurred


ERROR: ExecAppend: Fail to add null value in not null attribute phone

First pg_exec displays an error. After the error message you have displayed, the result of
pg_errormessage has been printed on the screen. As you can see, PostgreSQL complains that
a NULL value cannot be added to the table because the third column has been declared as NOT
NULL.
Take a look at an additional example:
<?php
$connstr = "dbname=phpbook user=postgres host=localhost";
$dbh = pg_connect($connstr);
if ($dbh) {echo "connection to phpbook established ...<br>";}

echo "first attempt<br>\n";


$stat = pg_exec($dbh, "INSERT INTO phone (name) VALUES ('Susan')");

echo "<br>second attempt<br>\n";


$stat = pg_exec($dbh, "INSERT INTO phone (name, phone) VALUES
('Susan', '3432434')");
if ($stat)
{
echo "data inserted successfully<br>\n";
echo "correct: ".pg_errormessage($dbh)."<br>";
}
else
{
echo "<br>an error has occurred<br>\n";
echo "wrong: ".pg_errormessage($dbh)."<br>";
}
pg_close($dbh);
?>

The first SQL statement sent to the server violates the constraint defined on the third column. The
second SQL statement is correct and the data will be added to the table. The most important thing
in this example is to see that pg_errormessage does not return an error if the previous SQL
statement has been executed successfully. This looks obvious but it isn't: In some database sys-
tems, functions always retrieve the most recent error even if a correct statement has been executed
after the error has occurred.
Let's take a look at the listing generated by the PHP script:
connection to phpbook established ...
first attempt

Warning: PostgreSQL query failed: ERROR: ExecAppend: Fail to add null value
in not null attribute phone in /var/www/html/createtab.php on line 7

second attempt
data inserted successfully
correct:

Chapter 11. Writing Database-Driven Applications. PHP and PostgreSQL: Advanced Web Programming, ISBN: 0-672-32382-6
Prepared for punfzr@hub2.nic.in, Dinesh Sharma
Copyright 2002 by Sams Publishing. This download file is made available for personal use only and is subject to the Safari Terms of Service. Any other use requires prior written consent
from the copyright owner. Unauthorized use, reproduction and/or distribution are strictly prohibited and violate applicable laws. All rights reserved.
Writing Database-Driven Applications 271

Sometimes you need to find out how many rows have been affected by a query. Especially when
performing UPDATE operations, this can be useful to find out what has happened inside the data-
base. The command used to extract the number of rows affected by a SQL statement is called
pg_cmdtuples. You can see how this command works in the next listing:
<?php
$connstr = "dbname=phpbook user=postgres host=localhost";
$dbh = pg_connect($connstr);
if ($dbh) {echo "connection to phpbook established ...<br>";}

$stat = pg_exec($dbh, "UPDATE phone SET phone='123456'


WHERE name='Susan'");

if ($stat)
{
echo "UPDATE successful<br>\n";
echo pg_cmdtuples($stat)."<br>";
}
pg_close($dbh);
?>

In this scenario the phone number of Susan is updated. In our database Susan has been inserted
once, so pg_cmdtuples has returned 1.
Let's see what is being displayed on the screen when the script is executed:
connection to phpbook established ...
UPDATE successful
1

The result is in no way surprising.


Error handling is an easy task when working with PHP and PostgreSQL. As you have seen in this
section, it takes only a few commands to perform almost all kinds of exception handling.

Handling Huge Amounts of Data


If you have to work with more than just a few records, using INSERT is far too slow. As you have
already learned, PostgreSQL provides a command called COPY, which you can use to insert more
than just one record into a table at once.
In this section the PHP functions related to the COPY command will be discussed. You will also learn
about the performance impact of using COPY.
Using COPY works slightly differently than working with a set of INSERT commands. Here's the next
example:
<?php
$connstr = "dbname=phpbook user=postgres host=localhost";
$dbh = pg_connect($connstr);
if ($dbh) {echo "connection to phpbook established ...<br>";}

echo "starting process ...<br>\n";


echo "begin: ".date('Y-m-d H:i:s')."<br>\n";
$result = pg_exec($dbh, "COPY testdata FROM stdin");
for ($i = 0; $i < 40000; $i++)
{
$stat = pg_put_line($dbh, "$i\t".(cos($i))."\n");
if (!$stat)
{
echo "an error has occurred<br>\n";
}
}
pg_put_line($dbh, "\\.\n");

Chapter 11. Writing Database-Driven Applications. PHP and PostgreSQL: Advanced Web Programming, ISBN: 0-672-32382-6
Prepared for punfzr@hub2.nic.in, Dinesh Sharma
Copyright 2002 by Sams Publishing. This download file is made available for personal use only and is subject to the Safari Terms of Service. Any other use requires prior written consent
from the copyright owner. Unauthorized use, reproduction and/or distribution are strictly prohibited and violate applicable laws. All rights reserved.
Writing Database-Driven Applications 272

pg_end_copy($dbh);
echo "end: ".date('Y-m-d H:i:s')."<br>\n";
pg_close($dbh);
echo "ending process ...<br>\n";
?>

Before running the script you have to create a table like that:
CREATE TABLE testdata (idx int4, val numeric(9,8));

After connecting to the database, the head of the COPY command is sent to the server. After that,
PostgreSQL is waiting for data until \. can be found. Every line of data is submitted to the database
by using the pg_put_line command. The table you want to add data to consists of two columns.
The first column contains a number and the second column contains the cosine of that number.
After the data has been sent to PostgreSQL, the COPY command is terminated by using
pg_end_copy. To see how fast the COPY command works, the current time is displayed on the
screen as well:
connection to phpbook established ...
starting process ...
begin: 2001-12-12 21:57:34
end: 2001-12-12 21:57:38
ending process ...

The entire COPY command takes not longer than four seconds, which is fast (the demo has been
run on a 466Mhz laptop). Because the entire set of data is added to the table in just one transaction,
there is hardly any overhead.
Let's delete the records from the table:
phpbook=# DELETE FROM testdata;
DELETE 40000

To understand why the COPY command should be used, the next listing shows how the same result
can be achieved by using INSERT commands:
<?php
$connstr = "dbname=phpbook user=postgres host=localhost";
$dbh = pg_connect($connstr);
if ($dbh) {echo "connection to phpbook established ...<br>";}

echo "starting process ...<br>\n";


echo "begin: ".date('Y-m-d H:i:s')."<br>\n";
for ($i = 0; $i < 40000; $i++)
{
$stat = pg_exec($dbh, "INSERT INTO testdata VALUES
($i, '".(cos($i))."')");
if (!$stat)
{
echo "an error has occurred<br>\n";
}
}
echo "end: ".date('Y-m-d H:i:s')."<br>\n";
pg_close($dbh);
echo "ending process ...<br>\n";
?>

Remember, using COPY took about four seconds to insert 40,000 records. The next listing shows
how long it takes when INSERT is used:

Chapter 11. Writing Database-Driven Applications. PHP and PostgreSQL: Advanced Web Programming, ISBN: 0-672-32382-6
Prepared for punfzr@hub2.nic.in, Dinesh Sharma
Copyright 2002 by Sams Publishing. This download file is made available for personal use only and is subject to the Safari Terms of Service. Any other use requires prior written consent
from the copyright owner. Unauthorized use, reproduction and/or distribution are strictly prohibited and violate applicable laws. All rights reserved.
Writing Database-Driven Applications 273

connection to phpbook established ...


starting process ...
begin: 2001-12-12 22:00:03
end: 2001-12-12 22:13:20
ending process ...

It takes more than 13 minutes, which is significantly slower. This impressive example shows how
much overhead you can save with the help of COPY.
The more data you have to process, the more performance you can gain. Technically,
pg_put_line does nothing more than send a NULL terminated string to the backend process
handling the connection to PostgreSQL. After sending the data to the database, the backend and
the frontend process have to be synchronized, which can be done by using pg_end_copy.

Retrieving Objects from the Database


When working with object-oriented PHP, it can be helpful to retrieve data from the table as objects.
Therefore PHP provides a command called pg_fetch_object, which can be used like
pg_fetch_array and pg_fetch_row. To see how the command works, first create a table and
insert some data:
phpbook=# CREATE TABLE plant(id int4, name text, color text);
CREATE
phpbook=# INSERT INTO plant VALUES (1, 'Sambucus nigra', 'yellow');
INSERT 116394 1
phpbook=# INSERT INTO plant VALUES (2, 'Abies', 'green');
INSERT 116395 1
phpbook=# INSERT INTO plant VALUES (3, 'Colchicum autumnale', 'pink');
INSERT 116396 1

If no error occurred, you have created a table containing information about three plants and their
colors. In the next step, the data should be retrieved as objects.
The next example shows pg_fetch_object in action:
<?php
$connstr = "dbname=phpbook user=postgres host=localhost";
$dbh = pg_connect($connstr);

$sql = "SELECT id, name, color FROM plant WHERE id=1";


$result = pg_exec($dbh, $sql);
$data = pg_fetch_object($result, 0);

echo "If you have the flu drink some tea made of $data->name<br>\n";
echo "Take the $data->color parts of the plant and boil it.<br>\n";

pg_close($dbh);
?>

To access the various components of the object, the -> operator must be used. The only difference
between using an array and using objects lies in the syntax you have to use.
Let's take a look at the output of the script:
If you have the flu drink some tea made of Sambucus nigra
Take the yellow parts of the plant and boil it.

The data is displayed just as if you were using arrays.

Chapter 11. Writing Database-Driven Applications. PHP and PostgreSQL: Advanced Web Programming, ISBN: 0-672-32382-6
Prepared for punfzr@hub2.nic.in, Dinesh Sharma
Copyright 2002 by Sams Publishing. This download file is made available for personal use only and is subject to the Safari Terms of Service. Any other use requires prior written consent
from the copyright owner. Unauthorized use, reproduction and/or distribution are strictly prohibited and violate applicable laws. All rights reserved.
Writing Database-Driven Applications 274

Tracing a PostgreSQL Connection


Especially for debugging purposes, tracing a connection to a PostgreSQL server can be useful.
Tracing a connection means that the data transmitted from and to the back end is monitored and
can be used to see what PostgreSQL does internally. In general, the information generated by a
trace is only useful if you are familiar with PostgreSQL's internal protocol. However, we need to see
how tracing can be turned on and off.
Take a look at the next example:
<?php
$dbh = pg_connect("host=localhost user=postgres dbname=phpbook");
if (!$dbh) { echo "error while connecting.<br>\n"; }

$status = pg_trace("/tmp/trace.log");
$result = pg_exec($dbh, "SELECT id, name, color FROM plant LIMIT 1");
$rows = pg_numrows($result);
for ($i = 0; $i < $rows; $i++)
{
$data = pg_fetch_row($result, $i);
echo "$data[0], $data[1], $data[2]<br>\n";
}
?>

The target is to select one record from the table called plant and to store the tracing information
in /tmp/trace.log. To turn on tracing, PHP provides a function called pg_trace. Let's see what
will be displayed on the screen when the script is executed:
1, Sambucus nigra, yellow

One line is displayed on the screen. In addition, the logfile has been created and a number of lines
have been added to it:
To backend> Q
To backend> SELECT id, name, color FROM plant LIMIT 1
From backend> P
From backend> "blank"
From backend> T
From backend (#2)> 3
From backend> "id"
From backend (#4)> 23
From backend (#2)> 4
From backend (#4)> -1
From backend> "name"
From backend (#4)> 25
From backend (#2)> 65535
From backend (#4)> -1
From backend> "color"
From backend (#4)> 25
From backend (#2)> 65535
From backend (#4)> -1
From backend> D
From backend (1)>
From backend (#4)> 5
From backend (1)> 1
From backend (#4)> 18
From backend (14)> Sambucus nigra
From backend (#4)> 10
From backend (6)> yellow
From backend> C
From backend> "SELECT"
From backend> Z
From backend> Z
To backend> X

Chapter 11. Writing Database-Driven Applications. PHP and PostgreSQL: Advanced Web Programming, ISBN: 0-672-32382-6
Prepared for punfzr@hub2.nic.in, Dinesh Sharma
Copyright 2002 by Sams Publishing. This download file is made available for personal use only and is subject to the Safari Terms of Service. Any other use requires prior written consent
from the copyright owner. Unauthorized use, reproduction and/or distribution are strictly prohibited and violate applicable laws. All rights reserved.
Writing Database-Driven Applications 275

As you can see, a lot of logging information has been written into the file. To use the logging infor-
mation effectively, you must take a closer look at PostgreSQL's internals.
To turn off tracing again, pg_untrace must be used.

Locking
If you are writing software for a frequently used Web server, you'll need to take a closer look at
locking. Usually locking is done by PostgreSQL internally. However, it is possible to influence locking
explicitly. In many situations this is necessary, and it can help you to get around problems easily.
The most important thing when working with explicit locking is to see how concurrent PHP scripts
treat locking.
In general, working with locks is easy. Everything can be done using just one command:
phpbook=# \h LOCK
Command: LOCK
Description: explicitly lock a table
Syntax:
LOCK [ TABLE ] name [, ...]
LOCK [ TABLE ] name [, ...] IN lockmode MODE

where lockmode is one of:

ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE |


SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE

As you can see, LOCK provides many different options for defining how to lock a table. One important
point is how PHP and PostgreSQL treat locks that are not released explicitly. In the next example
you will see how this works:
<?php
$dbh = pg_connect("host=localhost user=postgres dbname=phpbook");
if (!$dbh) { echo "error while connecting.<br>\n"; }

execute("INSERT INTO plant VALUES (4, 'pines', 'green')");


execute('LOCK plant IN EXCLUSIVE MODE');
execute("INSERT INTO plant VALUES (5, 'rose', 'red')");

function execute($sql)
{
global $dbh;
echo "start: ".date('Y-m-d H:i:s')." --- $sql<br>\n";
$status = pg_exec($dbh, $sql);
echo "end: ".date('Y-m-d H:i:s')." --- $sql<br>\n";
if (!$status)
{
echo "an error has occurred when executing ($sql)<br>\n";
}
}

?>

The function called execute combines the execution of a SQL statement with error handling. This
is helpful because error handling has to be implemented only once. In addition, the starting time
and the finishing time of the SQL statements is displayed. This information is necessary to see what
effects locking has.
First one record is added to the table. In the next step the table is locked exclusively and an additional
record is added to the table. The lock is not released, and the question now is: How will this affect
the further treatment of the table?
After executing the script the first time, two additional values can be found in plant:

Chapter 11. Writing Database-Driven Applications. PHP and PostgreSQL: Advanced Web Programming, ISBN: 0-672-32382-6
Prepared for punfzr@hub2.nic.in, Dinesh Sharma
Copyright 2002 by Sams Publishing. This download file is made available for personal use only and is subject to the Safari Terms of Service. Any other use requires prior written consent
from the copyright owner. Unauthorized use, reproduction and/or distribution are strictly prohibited and violate applicable laws. All rights reserved.
Writing Database-Driven Applications 276

phpbook=# SELECT * FROM plant;


id | name | color
----+---------------------+--------
1 | Sambucus nigra | yellow
2 | Abies | green
3 | Colchicum autumnale | pink
4 | pines | green
5 | rose | red
(5 rows)

The logging information displayed by the script contains nothing special:


start: 2001-12-14 10:06:25 --- INSERT INTO plant VALUES (4, 'pines', 'green')
end: 2001-12-14 10:06:25 --- INSERT INTO plant VALUES (4, 'pines', 'green')
start: 2001-12-14 10:06:25 --- LOCK plant IN EXCLUSIVE MODE
end: 2001-12-14 10:06:25 --- LOCK plant IN EXCLUSIVE MODE
start: 2001-12-14 10:06:25 --- INSERT INTO plant VALUES (5, 'rose', 'red')
end: 2001-12-14 10:06:25 --- INSERT INTO plant VALUES (5, 'rose', 'red')

Everything has been processed in one second. Let's see what will be displayed when the script is
executed a second time:
start: 2001-12-14 10:06:55 --- INSERT INTO plant VALUES (4, 'pines', 'green')
end: 2001-12-14 10:06:55 --- INSERT INTO plant VALUES (4, 'pines', 'green')
start: 2001-12-14 10:06:55 --- LOCK plant IN EXCLUSIVE MODE
end: 2001-12-14 10:06:55 --- LOCK plant IN EXCLUSIVE MODE
start: 2001-12-14 10:06:55 --- INSERT INTO plant VALUES (5, 'rose', 'red')
end: 2001-12-14 10:06:55 --- INSERT INTO plant VALUES (5, 'rose', 'red')

The lock does not affect the scripts started after the first one has been terminated. The reason for
that is simple: When the connection to PostgreSQL is closed by PHP automatically, all locks made
by this connection will be released automatically. However, PHP scripts that are accessing
plant between the time the first PHP script has locked the table and the script has been terminated
have to wait until the lock is released. If the script takes a long time to succeed, this will decrease
the performance of your system significantly.

PHP and Transactions


Transactions and locks are strongly related to each other. After you have seen how locks can be
treated, you can take a closer look at PHP and transactions. Usually working with transactions is
an easy task. However, a closer look is necessary to find out what happens to uncommitted trans-
actions. The next example shows what happens with transactions that are not committed manually:
<?php
$dbh = pg_connect("host=localhost user=postgres dbname=phpbook");
if (!$dbh) { echo "error while connecting.<br>\n"; }

execute("BEGIN;");
execute("INSERT INTO plant VALUES (6, 'cactus', 'green')");

function execute($sql)
{
global $dbh;
$status = pg_exec($dbh, $sql);
if (!$status)
{
echo "an error has occurred when executing ($sql)<br>\n";
}
}

?>

Chapter 11. Writing Database-Driven Applications. PHP and PostgreSQL: Advanced Web Programming, ISBN: 0-672-32382-6
Prepared for punfzr@hub2.nic.in, Dinesh Sharma
Copyright 2002 by Sams Publishing. This download file is made available for personal use only and is subject to the Safari Terms of Service. Any other use requires prior written consent
from the copyright owner. Unauthorized use, reproduction and/or distribution are strictly prohibited and violate applicable laws. All rights reserved.
Writing Database-Driven Applications 277

First a transaction is started. In the next step one value is added to the table, but the transaction is
not committed manually. Therefore, the new value cannot be found in the table:
phpbook=# SELECT * FROM plant WHERE id=6;
id | name | color
----+------+-------
(0 rows)

When the script has been executed, PHP closes the connection to PostgreSQL and the database
performs an implicit ROLLBACK. This way, no data from uncommitted transactions will pollute your
data.
The situation differs when the transaction is ended explicitly:
<?php
$dbh = pg_connect("host=localhost user=postgres dbname=phpbook");
if (!$dbh) { echo "error while connecting.<br>\n"; }

execute("BEGIN;");
execute("INSERT INTO plant VALUES (6, 'cactus', 'green')");
execute("COMMIT;");

function execute($sql)
{
global $dbh;
$status = pg_exec($dbh, $sql);
if (!$status)
{
echo "an error has occurred when executing ($sql)<br>\n";
}
}

?>

This time the record can be found in the table because the transaction has been committed correctly.
phpbook=# SELECT * FROM plant WHERE id=6;
id | name | color
----+--------+-------
6 | cactus | green
(1 row)

If you are not using BEGIN and COMMIT explicitly, every command will be executed as one separate
transaction. This is an important point because you don't have to deal with transactions explicitly.
As you have seen in this section, PHP does not provide onboard tools for handling transactions.
Everything is done by executing ordinary SQL statements.

Summary
PHP's programming interface to PostgreSQL is based on PostgreSQL's C library, and therefore the
PHP interface is similar to the C interface of PostgreSQL.
Inserting, retrieving, and selecting data can be done by sending SQL statements to the database.
To extract data from the result, PostgreSQL provides various methods. Depending on what you like
best, data can be retrieved as rows, records, or objects.

Chapter 11. Writing Database-Driven Applications. PHP and PostgreSQL: Advanced Web Programming, ISBN: 0-672-32382-6
Prepared for punfzr@hub2.nic.in, Dinesh Sharma
Copyright 2002 by Sams Publishing. This download file is made available for personal use only and is subject to the Safari Terms of Service. Any other use requires prior written consent
from the copyright owner. Unauthorized use, reproduction and/or distribution are strictly prohibited and violate applicable laws. All rights reserved.

You might also like