Friday, November 27, 2009

8.2 Issues in Writing Data to Databases











 < Day Day Up > 









8.2 Issues in Writing Data to Databases







In this section, we discuss

issues that emerge in web database applications when multiple users

access an application. Typically, a few users are inserting,

updating, or deleting data, while most are running queries. This

environment requires careful code design: without it, data can

unexpectedly or unreliably change. This may lead to database

inconsistencies and confused users.





Some of the problems we describe in this section can be solved with

restrictive system requirements, knowledge of how the DBMS behaves,

and careful script development. Others solutions require an

understanding of database theory. We discuss both types of solution

in the next section.









8.2.1 Transactions and Concurrency







Problems can

occur when users read and write to a web database at the same time,

that is, concurrently. The management of groups

of SQL statements that read and write, or

transactions

,

is one important area of the theory and practice of relational

databases. Here are four of the more common problems of concurrent

read and write transactions:












Lost update problem





User A reads from the database, recording a value. User B reads the

same value, then updates the value immediately. User A then updates

the value, overwriting the update written by User B.





Consider an example. Imagine that a winestore manager wants to order

one dozen more bottles of a popular wine, but only if there are less

than two dozen bottles currently in stock. The manager runs a query

to sum the total stock for that wine from the inventory. The result

is that there are fifteen bottles left, so the manager decides to

place an order. However, he heads off to fill his coffee cup first,

leaving the system displaying the query result.





A second stock manager arrives at her desk with the same intention:

to order more of this popular wine if there are less than two dozen

bottles. The result of the query is the same: fifteen bottles. The

second manager orders a dozen bottles, and updates the inventory to

27, knowing the bottles will arrive in the afternoon. The problem

occurs when the first manager returns: he doesn't

rerun the query and he too orders 12 bottles and updates the

inventory to 27. Now the system has record of 27 bottles, but 24 will

arrive in the afternoon to take the actual stock total to 39!






Dirty read problem







User A reads a

value from the database, changes the value, and writes it back to the

database. User B then reads the value, changes the value, and writes

it back to the database. User A then decides not to confirm the

changes for some reason and undoes the changes he made. The problem

is that User B has read and used the changed value, resulting in a

dirty read problem.





Consider an example. A manager decides to add a 3% surcharge to a

particular wine inventory, so she reads and updates the cost of that

wine. Another manager decides to apply a 10% discount to all wines

made by a particular winery, which happens to include the wine just

surcharged. After all this, the first manager realizes she has made a

mistake: the wrong wine was updated! Unfortunately, the second

manager has already used this incorrect value as input into his

update, and the change can't be undone correctly.






Incorrect summary problem







User A updates values while User B reads

and summarizes the same values. Values summarized may be read before

or after each individual update, resulting in unpredictable results.





Consider an example in the online winestore where a manager wants to

produce a management stock report. The report details wine sales,

winery sales, wine region sales, and total sales. The reporting

process has four steps: first, the sales of each wine are tallied;

second, the total sales of wines for each winery are tallied; third,

the total sales of wines for each region are tallied; and, last, the

overall total sales of wines is determined. The report uses four

queries and takes a few minutes to run.





Now, imagine that during this process, a customer purchases a bottle

of Paradise Pinot Noir wine from the Paradise Enough winery.

Specifically, let's imagine this happens after the

total sales of the Paradise Pinot Noir wine are tallied but before

the Paradise Enough winery sales are tallied. The result is that the

tally of the Pinot Noir's sales

doesn't include this purchase, but the tally of

Paradise Enough winery sales does. The result is an inconsistency:

adding together all of the wine sales won't give the

same value that's reported for the winery.






Unrepeatable read problem







A value

is read in by User A, updated by User B, and subsequently reread by

User A for verification. Despite not modifying the value, User A

encounters two different values, that is, the read operation is

unrepeatable.





Consider an example. Imagine a user of an online winestore wants to

buy the last bottle of an expensive, rare wine

that's in stock. He browses the database and finds

the wine. There is only bottle left, and he quickly adds this to his

shopping cart; in our implementation, this creates new rows in two

tables in the database. Now, he decides to finalize the purchase and

is presented with a summary of the shopping cart.





However, while the user fumbles about finding his password to log in,

another user enters the system. She quickly locates the same wine,

sees that there is only one bottle left, adds it to her shopping

cart, logs in to the system, and purchases the wine. When our first

user finally logs in to finalize the order, all the details look

fine, but the wine has actually been sold. Our database operation to

deduct from the inventory reports an error because the stock value is

already zero (the value has changed during the transaction), and we

end up reporting the error to our original (now very unhappy and

confused!) user.









Fortunately, most of these problems can be solved through

locking or careful design of scripts that carry

out database transactions. However, you might choose not to solve

some problems because they restrict the system requirements or add

unnecessary complexity. We discuss locking in the next

section.











8.2.2 Locking to Achieve Concurrency in MySQL





It has been shown that a simple scheme called

locking



(actually, it's correctly known as

two-phase locking

) solves the four transaction problems

identified in the last section.









8.2.2.1 When and how to lock tables




Locking is needed only when multiple steps must be performed

together, and when two or more operations can be going on at the same

time. If scripts are being implemented that write to the database but

aren't multi-step operations susceptible to the

problems described in the previous section, locks

aren't needed.





Specifically, the following situations do not require a lock:





  • Simple queries that insert rows, delete rows, or update rows, and

    that don't use results of a previous

    SELECT or data entered by the user as input. For

    example, updating a customer's details, adding a new

    phonebook entry, or unconditionally deleting a row do not require a

    lock.

  • Single-user applications or applications where only one user can

    alter the data do not require locks regardless of what queries are

    used.



The following situations do require locks:





  • Multi-user applications require locks, but only if either of the next

    two points are true.

  • A script first reads a value from a database and later writes that

    value to the database. For example, to create a row without using

    MySQL proprietary features, you first need to find the highest value

    used for the primary key using a SELECT and then

    INSERT a new row with the next available key

    value.

  • A script first writes a value to a database and later reads that

    value from the database. For example, to update and display an

    inventory, you might first add an extra quantity with an

    UPDATE and then read it back with a

    SELECT to check the total and show it to the user.



Locking may not be required for all parts of a web database

application: parts of the application can still be safely used

without violating any locking conditions.





With its default settings, each MySQL table has two associated lock

variables. If a user sets or holds a lock

variable for a particular table, no other user can perform particular

actions on that table. There are two kinds of locks for each table:

read locks, when a user is only reading from a table, and write

locks, when a user is both reading and writing to a table.





Having locks in a DBMS leads to four rules of use:





  • If a user wants to write to a table, and she is performing a

    transaction susceptible to a concurrency problem, she must obtain a

    write lock on that table.

  • If a user only wants to read from a table, and she is performing a

    transaction susceptible to a concurrency problem, she must obtain a

    read lock on that table.

  • If a user requires a lock, she must lock all tables used in the

    transaction in a single LOCK statement.

  • A user must release all locks when a database transaction is complete

    using the UNLOCK statement.



When a user holds a write lock on a table, no other users can read or

write to that table. When a user holds a read lock on a table, other

users can also read or hold a read lock, but no user can hold a write

lock on that table, or write to that table.







SELECT, UPDATE,

INSERT, or DELETE operations

that don't use LOCK

TABLES are held up if locks are held in other

transactions that would logically prevent their operation. For

example, if a user holds a write lock on a table, no other user can

issue a SELECT, UPDATE,

INSERT, DELETE, or

LOCK operation on that table.








The following segment of an interaction with the MySQL command

interpreter illustrates the use of locks in a summarization task that

requires locking:





mysql> LOCK TABLES items READ, temp_report WRITE;



mysql> SELECT sum(price) FROM items WHERE cust_id=1;

+------------+

| sum(price) |

+------------+

| 438.65 |

+------------+

1 row in set (0.04 sec)



mysql> UPDATE temp_report SET purchases=438.65

WHERE cust_id=1;

mysql> UNLOCK TABLES;







In this example, a temporary table called

temp_report is updated with the result of a

SELECT operation on an items

table. If locks aren't used, the

items table can be modified by another user,

possibly altering the summary value of $438.65 used as input to the

UPDATE operation. There are two locks obtained for

this transaction: first, a read lock on items

because we don't need to change

items but we don't want another

user to make a change to it; and, second, a write lock on

temp_report because we want to change the table,

and we don't want other users to read or write to

the report while we make changes. The

UNLOCK

TABLES operation

releases all locks held; locks can't be

progressively released.





MySQL doesn't permit us to lock only one of the two

tables used in the transaction above. The following rules apply to

locks:





  • If a lock is held, all other tables that are to be used must also be

    locked. Failing to do so results in a MySQL error.

  • If aliases are used in queries, the alias must be locked. For

    example, in the following query:

    SELECT * from customer c where c.custid=1



    the alias must be locked with one of:

    LOCK TABLES customer c READ



    or:

    LOCK TABLES customer c WRITE



    If different aliases for the same table are used, each different

    alias must be locked. Aliases are discussed in Chapter 15.



In many cases,



locking can be avoided

through careful query design:





  • Use MySQL's auto_increment

    feature to create new primary key values. Alternatively, use PEAR

    DB's DB::nextId( ) method that

    we discuss later in this chapter.

  • Use mysql_insert_id( ) (as opposed to using the

    max( ) function in a SELECT

    query) to find the value of a newly-created primary key. Again, PEAR

    DB's DB::nextId( ) method can

    be alternatively used.

  • Use advanced features of SQL to combine two queries into one; these

    features are discussed in Chapter 15. For

    example, you can use a single nested query to discover the total

    value in our previous example and then use that to create a new row

    in the temporary report.

  • Perform updates that are relative. For example, UPDATE

    customer SET discount =
    discount*1.1.











8.2.2.2 The LOCK TABLES and UNLOCK TABLES statements in MySQL




The LOCK

TABLES statement

is used to lock the listed tables in either READ

or WRITE mode. As discussed earlier, all tables

that are accessed in the transaction must be locked in either

READ or WRITE mode, and must be

listed in a single LOCK TABLES

statement.





A script that issues a LOCK

TABLES statement is suspended until all locks

listed are successfully obtained. There is no time limit in waiting

for locks. If the lock is held by another user or an operation is

running on the table already, a request is placed at the back of

either the write- or

read-lock queue for the

table, depending on the lock required. The write-lock queue has

priority over the read-lock queue, so a user who wants a write lock

obtains it when it becomes available, regardless of how long another

user has been waiting in the read-lock queue.





MySQL gives priority to database modifications over read queries.

This can lead to a problem called

starvation

,

where a transaction never completes because it can't

obtain its required read locks. However, most web database

applications read from databases much more than they write, and locks

are required in only a few situations, so starvation is very uncommon

in practice.





If low-priority writing is essential to an application, a

LOW_PRIORITY option can be prefixed before the

WRITE clause. If a transaction is queued for a

LOW_PRIORITY WRITE

, it receives the lock only when the read

lock queue is empty and no other users are reading from the table.

Again, consideration of possible starvation is important.





Locks can't be progressively obtained through

several LOCK

TABLES statements.

Indeed, issuing a second LOCK

TABLES is the same as issuing an

UNLOCK TABLES to release all

locks and then issuing the second LOCK

TABLES. There are good reasons for this strict

rule, related to a locking problem called

deadlock, which we don't

discuss here. However, MySQL is deadlock-free because it enforces the

risk-free use of the LOCK TABLES and

UNLOCK TABLES statements.





If an unlocked table needs to be accessed or locking must be avoided

for a particular table, a second server connection can be opened and

used.







MySQL has a feature called

INSERT



DELAYED for

insertion that is described in the MySQL manual.





Don't use locking with INSERT

DELAYED for insert operations. The

INSERT DELAYED process is

carried out by the MySQL server at a later time and the locks held by

the user can't be used by the server.
















8.2.2.3 Locking for performance






Locking is primarily designed to ensure

that concurrent transactions can execute safely. However, locking is

also a useful performance tool to optimize the performance of

important transactions.





Consider, for example, a situation where we urgently require a

complex report that uses a slow query. With other users running

queries and using system resources, this query may run even slower. A

solution is to use LOCK TABLES

with the WRITE option to stop other users running

queries or database updates, and to have exclusive access to the

database for the query duration. This permits better optimization of

the query processing by the server, dedication of all the system

resources to the query, and faster disk access.





The downside of locking for performance is the reduction in

concurrent access to the database. Users may be inconvenienced by

slow responses or timeouts from the web database application. Locking

for performance should be used sparingly.













8.2.3 Locking Tables in Web Database Applications





Example 8-13

shows a

PHP script that requires locking to ensure that the value returned

from the SELECT query can't

change before the INSERT operation. The script

adds a row to the phonebook table and does

exactly same thing as Example 8-9. However, it

doesn't use the MySQL proprietary

auto_increment modifier and so it needs to read

the maximum primary key value that's in use and then

write a new row based on that value.





Without the auto_increment modifier and with no

locking, it's possible that two rows could be

created with the same phonebook_id. This can

happen if two or more users run the script at the same time and get

the same result from the SELECT query. Both users

would then attempt to INSERT a new row with the

same primary key value and, if this happens, MySQL will report an

error because the primary key value must be unique. Locking solves

the problem because it stops users running the queries in the script

at the same time.









Example 8-13. Creating a phonebook entry using locking


<?php

require 'db.inc';

require_once "HTML/Template/ITX.php";



function formerror(&$template, $message, &$errors)

{

$errors = true;

$template->setCurrentBlock("error");

$template->setVariable("ERROR", $message);

$template->parseCurrentBlock("error");

}



if (!($connection = @ mysql_connect("localhost", "fred", "shhh")))

die("Could not connect to database");



$firstname = mysqlclean($_POST, "firstname", 50, $connection);

$surname = mysqlclean($_POST, "surname", 50, $connection);

$phone = mysqlclean($_POST, "phone", 20, $connection);



$template = new HTML_Template_ITX("./templates");

$template->loadTemplatefile("example.8-10.tpl", true, true);



$errors = false;



if (empty($firstname))

formerror($template, "The first name field cannot be blank.", $errors);



if (empty($surname))

formerror($template, "The surname field cannot be blank.", $errors);



if (empty($phone))

formerror($template, "The phone field cannot be blank", $errors);



// Now the script has finished the validation, show any errors

if ($errors)

{

$template->show( );

exit;

}



// If we made it here, then the data is valid

if (!mysql_select_db("telephone", $connection))

showerror( );



// Lock the table

$query = "LOCK TABLES phonebook WRITE";

if (!(@ mysql_query ($query, $connection)))

showerror( );



// Find the maximum phonebook_id value that's in use

$query = "SELECT max(phonebook_id) FROM phonebook";

if (!($result = @ mysql_query ($query, $connection)))

showerror( );



$row = @ mysql_fetch_array($result);



// Set the new value for the primary key

$phonebook_id = $row["max(phonebook_id)"] + 1;



// Insert the new phonebook entry

$query = "INSERT INTO phonebook VALUES

({$phonebook_id}, '{$surname}', '{$firstname}', '{$phone}')";

if (!(@ mysql_query ($query, $connection)))

showerror( );



// Unlock the table

$query = "UNLOCK TABLES";

if (!(@ mysql_query ($query, $connection)))

showerror( );



// Show the phonebook receipt

header("Location: example.8-5.php?status=T&phonebook_id={$phonebook_id}");

?>









The locking of the phonebook table is performed

before the SELECT query, and the

UNLOCK TABLES statement is

issued after the INSERT. As you can see, the lock

and unlock statements are executed just like any other query using

mysql_query( ).









8.2.3.1 Locking methods that don't work in web database applications






There are several locking

paradigms that don't work in a web database

application because of the statelessness of HTTP. Each approach fails

because there is either no guarantee or no possibility that the

locked tables will be unlocked. If tables are locked indefinitely,

other transactions can't proceed, and the DBMS will

most likely need to be shut down and restarted.







Be careful with locking in web database applications. Remember the

basic rule that all locks should be unlocked by the same script

during the same execution of the script.





All web scripts that require locking should have the sequence 1)

lock, 2) query, 3) update, delete, or insert, and 4) unlock. There

must be no user interaction or intervening calls to other scripts

that require input.








The following must be avoided in web database applications:





  • Failing to issue an

    UNLOCK

    TABLES on a locked

    persistent database connection (such as one that opened with

    mysql_pconnect( )). The locks

    aren't released when the script terminates.

    It isn't necessary to issue an

    UNLOCK TABLES if a

    nonpersistent connection is used (such as one opened with

    mysql_connect( )). Locks are automatically

    released when the script finishes and the connection closes. However,

    it is good practice to include the UNLOCK

    TABLES statement.

  • Locking one or more tables during the first execution of a script,

    leaving them locked, and then querying or updating during a second or

    subsequent execution of the script. Remember that each database

    connection in a script is independent and is treated as a different

    user by MySQL.

  • Retrieving a value such as the next available primary key value,

    presenting this to the user, waiting for the user to enter further

    details, and then adding a row to the database with that identifier.

    Remember that another user may add a row while the first user is

    entering the required details, and locks should never be carried

    across several scripts or different executions of the same script.











8.2.3.2 Locking with an auxiliary table






Locking limits concurrency in your web

database application. If tables are locked, then other users

won't be able to run the same script at the same

time and other scripts may also not be able to proceed. For example,

suppose you write lock the phonebook table

we've used in our examples throughout this chapter.

With the table locked, any other query on the

phonebook table in any script

won't proceed until you unlock the table; this

means, for example, while you insert one row, no other users can

search for a phone number. Sometimes, you want to avoid this and this

section shows you how.





One technique you can use to minimize locking of your frequently used

tables is to add an additional table to the database. This additional

table stores and manages the next available primary key values for

all other tables in the database. The additional table is then

locked, queried, updated, and unlocked each time a new primary key

value is needed; the main tables in the database are then never

locked when data is inserted. In the remainder of this section, we

show you how to do this using the MySQL function library; the next

section shows you how to do the same thing using PEAR DB.







Let's consider an

example. Suppose you want to add new rows to the

phonebook table without locking it and without

using the proprietary MySQL auto_increment

modifier. You first create an additional table in the

telephone database using the following

CREATE TABLE statement:





CREATE TABLE identifiers (phonebook_id int(5));







As we show you next, this table only contains one row and therefore

there's no need to declare or use a primary key.





The new

identifiers

table stores one row that contains the

next available value of the phonebook_id primary

key attribute from the phonebook table. To set

this up, you add the row to the table and set the

phonebook_id attribute to the next available

value. Let's suppose your

phonebook table is empty, and so the next

primary key value for phonebook_id is 1.

Here's the INSERT statement you

use to set up the table:





INSERT INTO identifiers VALUES (1);







Now you can use the identifiers table to read

and write a primary key value for the phonebook

table. Having done this, you use the primary key value to create a

new row without locking the phonebook table.

Here's how you do it using the MySQL command

interpreter:





mysql> LOCK TABLES identifiers WRITE;

Query OK, 0 rows affected (0.00 sec)



mysql> SELECT phonebook_id FROM identifiers;

+--------------+

| phonebook_id |

+--------------+

| 1 |

+--------------+

1 row in set (0.00 sec)



mysql> UPDATE identifiers SET phonebook_id = phonebook_id + 1;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1 Changed: 1 Warnings: 0



mysql> UNLOCK TABLES;

Query OK, 0 rows affected (0.00 sec)



mysql> INSERT INTO phonebook VALUES (1, "Williams",

"Lucy", "61388763452");

Query OK, 1 row affected (0.01 sec)







The locking, querying, modifying, and unlocking process proceeds

similarly to our example in the previous section, except that it

doesn't use the phonebook

table. Instead, the new row is inserted into the

phonebook table without a lock using the value

discovered with the SELECT query from the

identifiers table, thereby maximizing

concurrency (but requiring three SQL queries instead of two). Example 8-14 shows a rewritten version of Example 8-13 that uses this approach.









Example 8-14. Maintaining the phonebook table using an external identifiers table


<?php

require 'db.inc';

require_once "HTML/Template/ITX.php";



function formerror(&$template, $message, &$errors)

{

$errors = true;

$template->setCurrentBlock("error");

$template->setVariable("ERROR", $message);

$template->parseCurrentBlock("error");

}



if (!($connection = @ mysql_connect("localhost", "fred", "shhh")))

die("Could not connect to database");



$firstname = mysqlclean($_POST, "firstname", 50, $connection);

$surname = mysqlclean($_POST, "surname", 50, $connection);

$phone = mysqlclean($_POST, "phone", 20, $connection);



$template = new HTML_Template_ITX("./templates");

$template->loadTemplatefile("example.8-10.tpl", true, true);



$errors = false;



if (empty($firstname))

formerror($template, "The first name field cannot be blank.", $errors);



if (empty($surname))

formerror($template, "The surname field cannot be blank.", $errors);



if (empty($phone))

formerror($template, "The phone field cannot be blank", $errors);



// Now the script has finished the validation, show any errors

if ($errors)

{

$template->show( );

exit;

}



// If we made it here, then the data is valid

if (!mysql_select_db("telephone", $connection))

showerror( );



// Lock the identifiers table

$query = "LOCK TABLES identifiers WRITE";

if (!(@ mysql_query ($query, $connection)))

showerror( );



// Find the maximum phonebook_id value that's in use

$query = "SELECT phonebook_id FROM identifiers";

if (!($result = @ mysql_query ($query, $connection)))

showerror( );



$row = @ mysql_fetch_array($result);



$phonebook_id = $row["phonebook_id"];



// Update the phonebook_id identifier

$query = "UPDATE identifiers SET phonebook_id = phonebook_id + 1";

if (!($result = @ mysql_query ($query, $connection)))

showerror( );



// Unlock the table

$query = "UNLOCK TABLES";

if (!(@ mysql_query ($query, $connection)))

showerror( );



// Insert the new phonebook entry

$query = "INSERT INTO phonebook VALUES

({$phonebook_id}, '{$surname}', '{$firstname}', '{$phone}')";

if (!(@ mysql_query ($query, $connection)))

showerror( );



// Show the phonebook receipt

header("Location: example.8-5.php?status=T&phonebook_id={$phonebook_id}");

?>









To extend this scheme for a database containing several tables, there

are two possible approaches: first, add an additional attribute (or

more than one attribute if the primary key isn't on

only one attribute) to the identifiers table for

each additional table; or, second, add an additional identifier table

for each additional table. The first approach is the simplest (and

the one we recommend) but it does have the potential disadvantage

that concurrency could be limited by excessive locking of the

identifiers table if too many tables are

maintained by using it. The second approach maximizes concurrency but

is probably only necessary for high-throughput applications.













8.2.3.3 Managing identifiers with PEAR DB






In the previous section, we showed you how

to maintain identifiers using an additional table. PEAR DB allows you

to do the same thing using its DB::nextId(

)


method and this is useful if you want to

write database independent code. We show you how to use it in this

section. The PEAR DB sequence methods are also briefly described in

Chapter 7.





A sequence is a value associated with a name and

it's typically used to create primary key values. A

sequence is always initialized to 1, and increments each time you

access it with DB::nextId( ). For example,

suppose you want to maintain the primary key value for the

phonebook_id from the

phonebook table that we've used

in our examples in this chapter. To do this, you can use the

DB::nextID( ) method as shown in Example 8-15:





// Get a new primary key value for phonebook_id

$phonebook_id = $connection->nextId("phonebook_id");







When this is called for the first time, DB::nextId(

)
creates a new sequence named

phonebook_id, assigns it the value 1, and returns

the value. When you call it for the second time, it returns 2, and so

on. It performs exactly the same function as our

identifiers table approach in Example 8-14.









Example 8-15. Using PEAR DB to maintain primary key values


<?php

require "db.inc";

require_once "HTML/Template/ITX.php";

require_once "DB.php";



function formerror(&$template, $message, &$errors)

{

$errors = true;

$template->setCurrentBlock("error");

$template->setVariable("ERROR", $message);

$template->parseCurrentBlock("error");

}



$dsn = "mysql://fred:shhh@localhost/telephone";



$connection = DB::connect($dsn, false);

if (DB::isError($connection))

die($connection->getMessage( ));



$firstname = mysqlclean($_POST["firstname"], 50, $connection);

$surname = mysqlclean($_POST["surname"], 50, $connection);

$phone = mysqlclean($_POST["phone"], 20, $connection);



$template = new HTML_Template_ITX("./templates");

$template->loadTemplatefile("example.8-10.tpl", true, true);



$errors = false;



if (empty($firstname))

formerror($template, "The first name field cannot be blank.", $errors);



if (empty($surname))

formerror($template, "The surname field cannot be blank.", $errors);



if (empty($phone))

formerror($template, "The phone field cannot be blank", $errors);



// Now the script has finished the validation, show any errors

if ($errors)

{

$template->show( );

exit;

}



// Get a new primary key value for phonebook_id

$phonebook_id = $connection->nextId("phonebook_id");

if (DB::isError($connection))

die($connection->getMessage( ));



// Insert the new phonebook entry

$query = "INSERT INTO phonebook VALUES

({$phonebook_id}, {$surname}, {$firstname}, {$phone})";

$result = $connection->query($query);

if (DB::isError($result))

die($result->getMessage( ));



// Show the phonebook receipt

header("Location: example.8-5.php?status=T&phonebook_id={$phonebook_id}");

?>









Behind the scenes, PEAR DB maintains a sequence in a table of the

same name. When you create a sequence, it creates a table and an

attribute and initializes the attribute to 1. When you call

DB::nextId( ), it adds 1 and returns the value.

PEAR DB correctly looks after safe concurrent access.





If you call DB::nextID( ) without its optional

second parameter or with the second parameter set to

true, a sequence with the name supplied as the

first parameter is created if it doesn't exist. You

can also manually create a sequence using

DB::createSequence( ) and you can remove it

using DB::dropSequence( ).



























     < Day Day Up > 



    No comments: