= Programming with SQL Relay using the PHP PDO API =

* [#intro Introduction]
* [#config Configuring PHP]
* [#session Establishing a Session]
* [#query Executing Queries]
* [#transactions Transactions]
* [#temptables Temporary Tables]
* [#errors Catching Errors]
* [#bindvars Bind Variables]
* [#rebinding Re-Binding and Re-Executing]
* [#fields Accessing Fields in the Result Set]
* [#largeresultsets Dealing With Large Result Sets]
* [#statements Statements]
* [#columns Getting Column Information]
* [#storedprocedures Stored Procedures]
* [#lastinsertid Getting the Last Insert ID]
* [#driverspecific Driver-Specific Attributes]
* [#drivermethods Driver-Specific Methods]

[=#intro]
== Introduction ==

PDO is a popular database abstraction layer for PHP.

A [http://www.php.net/manual/en/book.pdo.php good PDO guide and reference] can be found on the PHP site but a short discussion of it follows here as well.

[=#config]
== Configuring PHP ==

To use the SQL Relay PDO driver, PHP must be configured to load the
appropriate module - pdo_sqlrelay.so on Linux or Unix and php_pdo_sqlrelay.dll
on Windows.

If you are using SQL Relay version 0.58 or higher on reasonably modern Linux
or Solaris with PHP installed from a package, then the installation process
should have configured PHP correctly and you can probably ignore the rest of
this section.

But on other operating sytems, or if you built PHP from source, or if the
installation process didn't go smoothly, then you may have to configure PHP
manually.

The PHP configuration file is usually named php.ini.  The name and location
of the file may vary though.  Here are some common locations, by platform.

* '''Linux''' - /etc/php.ini
* '''Solaris''' - /etc/php/&lt;php version&gt;/php.ini
* '''FreeBSD''' - /usr/local/etc/php.ini
* '''NetBSD''' - /usr/pkg/etc/php.ini
* '''OpenBSD''' - /etc/php-&lt;php version&gt;.ini
* '''Windows''' - C:\Windows\php.ini

To load the php module on Linux or Unix, you usually just need to add lines
to the configuration file like:

{{{#!blockquote
{{{#!code
extension=pdo.so
extension=pdo_sqlrelay.so
}}}
}}}

To load the php module on Windows, you usually need to add lines like:

{{{#!blockquote
{{{#!code
extension_dir=C:\PHP\ext
extension=php_pdo.dll
extension=php_pdo_sqlrelay.dll
}}}
}}}

(the //extension_dir// value might need to be changed, depending on where PHP is installed on your system)

It is possible, even likely that the configuration file already contains a line to load the pdo.so (or php_pdo.dll) module, so look through the file and verify whether it is or not before loading it again.

Also, some Linux and Unix distributions have PDO compiled directly into the PHP module or binary and don't require pdo.so module to be loaded at all.  If you built PHP from source with the default options then this is likely.  You can verify this by running:

{{{#!blockquote
{{{#!code
strings <path-to-php-binary>/php | grep "PDO drivers"
}}}
}}}

If PDO is built-in then it should return something like:

{{{#!blockquote
{{{
You MUST load PDO before loading any PDO drivers
}}}
}}}

And if that wasn't complex enough, some PHP distributions on Linux or Unix
have configuration directories.  PHP parses the php.ini file first, then goes
alphabetically through the files in the configuration directory.  The
configuration directory is usually named conf.d and is found somewhere under the
directory that the main configuration file is found in.  If there is a pdo.ini
file in there, or if one of the files in that directory loads the pdo.so module,
then you'll have to either add the lines above to the end of that same file or
create a new pdo_sqlrelay.ini file that contains the lines to load
pdo_sqlrelay.so or php_pdo_sqlrelay.dll

[=#session]
== Establishing a Session ==

To use SQL Relay, you have to identify the connection that you intend to 
use.

{{{#!blockquote
{{{#!code
@parts/phppdo-session.php@
}}}
}}}

The following connect string variables control attributes of the connection:

* '''host''' - The SQL Relay server to connect to.
* '''port''' - The port that the SQL Relay server is listening on.  Either port or socket must be specified.  If both are specified, socket will be preferred to port.
* '''socket''' - The socket that the SQL Relay server is listening on.  Either port or socket must be specified.  If both are specified, socket will be preferred to port.
* '''tries''' - If a connection fails, it will be retried this many times.
* '''retrytime''' - If a connection fails, it will be retried on this interval (in seconds).
* '''db''' - The database to switch to when logging into SQL Relay.  May be left empty or omitted to accept the default database that SQL Relay is already connected to.
* '''debug''' - If this is set to 1 or to a file name then debug is enabled.  If set to a file name then debug will be written to the file.  If set to 1 then debug will be written to the browser.  Defaults to 0.
* '''lazyconnect''' - By default, the SQL Relay client does "lazy connects".  I.e. the connection to the SQL Relay server isn't established until the first query or other operation is run.  This optimizes availability of the connection pool but is inconstent with most other database API's.  Setting this parameter to 0 causes the connection to the SQL Relay server to be established immediately and the constructor to fail, returning 0, if the connection cannot be established.  Defaults to 1.

In addition, the following connect string variables can be used to establish Kerberos or Active Directory encryption and authentication with the server:

See the [../admin/configguide.html#krb SQL Relay Configuration Guide] for more information about Kerberos and Active Directory configurations.  In particular, the user and password are usually left as empty strings or null when using Kerberos/AD.

* '''krb''' - Set to '''yes''' to enable Kerberos/AD encryption/authentication or '''no''' to disable it.
* '''krbservice''' - Specifies the Kerberos service name of the SQL Relay server.  If omitted or left empty then the service name "sqlrelay" will be used. "sqlrelay" is the default service name of the SQL Relay server.  Note that on Windows platforms the service name must be fully qualified, including the host and realm name.  For example: "sqlrelay/sqlrserver.firstworks.com@AD.FIRSTWORKS.COM".
* '''krbmech''' - Specifies the Kerberos mechanism to use.  On Linux/Unix platforms, this should be a string representation of the mechnaism's OID, such as: { 1 2 840 113554 1 2 2 }.  On Windows platforms, this should be a string like: Kerberos.  If omitted or left empty then the default mechanism will be used.  Only set this if you know that you have a good reason to.
* '''krbflags''' - Specifies what Kerberos flags to use.  Multiple flags may be specified, separated by commas.  If left empty or NULL then a defalt set of flags will be used.  Only set this if you know that you have a good reason to.  Valid flags include: GSS_C_MUTUAL_FLAG, GSS_C_REPLAY_FLAG, GSS_C_SEQUENCE_FLAG, GSS_C_CONF_FLAG, and GSS_C_INTEG_FLAG.  For a full list of flags, consult the GSSAPI documentation, though note that only the previously listed flags are supported on Windows.

The following connect string variables can be used to establish TLS/SSL encryption and authentication with the server:

See the [../admin/configguide.html#tls SQL Relay Configuration Guide] for more information about TLS/SSL configurations.

* '''tls''' - Set to '''yes''' to enable TLS/SSL encryption/authentication or '''no''' to disable it.
* '''tlsversion''' - Specifies the TLS/SSL protocol version that the client will attempt to use.  Valid values include SSL2, SSL3, TLS1, TLS1.1, TLS1.2 or any more recent version of TLS, as supported by and enabled in the underlying TLS/SSL library.  If omittted or left empty then the highest supported version will be negotiated.
* '''tlscert''' - Specifies the file name of the certificate chain file to send to the SQL Relay server.  This is only necessary if the SQL Relay server is configured to authenticate and authorize clients by certificate.
* '''tlspassword''' - If tlscert contains a password-protected private key, then tlspassword may be supplied to access it.  If the private key is not password-protected, then this argument is ignored, and may be omitted or left empty.
* '''tlsciphers''' - Specifies a list of ciphers to allow.  Ciphers may be separated by spaces, commas, or colons.  If omitted or left empty then a default set is used.  For a list of valid ciphers on Linux/Unix platforms, see: man ciphers. For a list of valid ciphers on Windows platforms, see [https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549%28v=vs.85%29.aspx this page].  On Windows platforms, the ciphers (alg_id's) should omit CALG_ and may be given with underscores or dashes.  For example: 3DES_112.  Only set this if you know that you have a good reason to.
* '''tlsvalidate''' - Indicates whether to validate the SQL Relay server's certificate, and may be set to one of the following: '''no''' - Don't validate the server's certificate.  '''ca''' - Validate that the server's certificate was signed by a trusted certificate authority.  '''ca+host''' - Perform "ca" validation and also validate that one of the subject altenate names (or the common name if no SANs are present) in the certificate matches the host parameter.  (Falls back to "ca" validation when a unix socket is used.)  '''ca+domain''' - Perform "ca" validation and also validate that the domain name of one of the subject alternate names (or the common name if no SANs are present) in the certificate matches the domain name of the host parameter.  (Falls back to "ca" validation when a unix socket is used.)
* '''tlsca''' - Specifies the location of a certificate authority file to use, in addition to the system's root certificates, when validating the SQL Relay server's certificate.  This is useful if the SQL Relay server's certificate is self-signed.  On Windows, "ca" must be a file name.  On non-Windows systems, "ca" can be either a file or  directory name.  If it is a directory name, then all certificate authority files found in that directory will be used.  If it a file name, then only that file will be used.

Note that the supported tlscert and tlsca file formats may vary between platforms.  A variety of file formats are generally supported on Linux/Unix platfoms (.pem, .pfx, etc.) but only the .pfx format is currently supported on Windows.

These additional connect string variables control the default statement attributes:

* '''resultsetbuffersize''' - Sets the number of rows to buffer at a time.  See [#largeresultsets Dealing With Large Result Sets] below.  If set to 0 (the default) then the entire result set is buffered.
* '''columnnamecase''' - Indicates the case to convert column names to.  May be set to "mixed", "upper", or "lower".  Defaults to "mixed".
* '''dontgetcolumninfo''' - If this is set to 1 then column info is not fetched with the result set.  If set to 0 then column info is fetched.  Defaults to 0.
* '''nullsasnulls''' - If this is set to 1 then NULL values are returned as nulls.  If set to 0 then NULL values are returned as empty strings.  Defaults to 0.

Setting one of the statement attribute connect string variables has the same effect as if the corresponding statement handle attribute were set immediately after creating each statement.  See [#driverspecific Driver-Specific Attributes] below.

The other two parameters following the connect string are.

* '''user''' - The username to use when logging into SQL Relay.
* '''password''' - The password to use when logging into SQL Relay.

After calling the constructor, a session is established when the first 
query or other operation is run, unless lazyconnect=0 is used, in which case
a session is established immediately.

For the duration of the session, the client occupies one of the database
connections, so care should be taken to minimize the length of a
session.

[=#query]
== Executing Queries ==

Call exec() to run a query and return the number of rows affected.  This is
best used with DML or DDL queries.

{{{#!blockquote
{{{#!code
@parts/phppdo-execute.php@
}}}
}}}

Call query() to run a query and return a statement object which can be used
to fetch the result set.  This is best used with selects or other queries that
return result sets.

{{{#!blockquote
{{{#!code
@parts/phppdo-query.php@
}}}
}}}

[=#transactions]
== Transactions ==

Run begin() to start a transaction.  To complete a transaction, run commit()
or rollBack().  You should use the commit() and rollBack() functions rather than
sending a "commit" or "rollback" query.  There are two reasons for this.  First,
it's much more efficient to call the functions.  Second, if you're writing code
that can run on transactional or non-transactional databases, some
non-transactional databases will throw errors if they receive a "commit" or
"rollback" query, but by calling the commit() and rollBack() functions you
instruct the database connection daemon to call the commit and rollback API
functions for that database rather than issuing them as queries.  If the API's
have no commit or rollback functions, the calls do nothing and the database
throws no error.

{{{#!blockquote
{{{#!code
@parts/phppdo-tx.php@
}}}
}}}

You can also turn Autocommit on or off using the setAttribute() method.

The following command turns Autocommit on.

{{{#!blockquote
{{{#!code
@parts/phppdo-autocommiton.php@
}}}
}}}

The following command turns Autocommit off.

{{{#!blockquote
{{{#!code
@parts/phppdo-autocommitoff.php@
}}}
}}}

When Autocommit is on, the database performs a commit after each successful
DML or DDL query.  When Autocommit is off, the database commits when the client
instructs it to, or (by default) when a client disconnects.  For databases that
don't support Autocommit, setting Autocommit on or off has no effect.

[=#temptables]
== Temporary Tables ==

Some databases support temporary tables.  That is, tables which are
automatically dropped or truncated when an application closes its connection
to the database or when a transaction is committed or rolled back.

For databases which drop or truncate tables when a transaction is committed
or rolled back, temporary tables work naturally.

However, for databases which drop or truncate tables when an application
closes its connection to the database, there is an issue.  Since SQL Relay
maintains persistent database connections, when an application disconnects from
SQL Relay, the connection between SQL Relay and the database remains, so
the database does not know to drop or truncate the table.  To remedy this
situation, SQL Relay parses each query to see if it created a temporary table,
keeps a list of temporary tables and drops (or truncates them) when the
application disconnects from SQL Relay.  Since each database has slightly
different syntax for creating a temporary table, SQL Relay parses each query
according to the rules for that database.

In effect, temporary tables should work when an application connects to
SQL Relay in the same manner that they would work if the application connected
directly to the database.

[=#errors]
== Catching Errors ==

Most PDO methods return a value indicating that they failed.  There are 3
options for error handling too that can be set using setAttribute().

* PDO::ERRMODE_WARNING - raise an E_WARNING on error
* PDO::ERRMODE_EXCEPTION - throw an exception on error
* PDO::ERRMODE_SILENT - just set error codes

After determining that an error has occurred you can find out why by
calling errorCode() or errorInfo() on the database handle or statement
objects.

{{{#!blockquote
{{{#!code
@parts/phppdo-errors.php@
}}}
}}}

[=#bindvars]
== Bind Variables ==

Programs rarely execute fixed queries.  More often than not, some part
of the query is dynamically generated.  The PDO API provides means for
using bind variables (also known as parameters) in those queries.

For a detailed discussion of substitutions and binds, see
[binds.html this document].

PDO provides several means for using bind variables but in all cases, you
must bind variable/value pairs to the statement.  Here is an example of using
the bindValue() method.

{{{#!blockquote
{{{#!code
@parts/phppdo-bind.php@
}}}
}}}

(Note: these examples use colon-delimited variable names.  These are not supported by all databases.  See "A few words about the format of bind variables..." below for more information.)

PDO supports binding integer, string and LOB values.  Oddly, it doesn't
support binding floating point values.  You have to pass them as strings.
Also, CLOB's have to be passed as LOB's.

PDO also provides a bindParam() method and allows you to pass an associative
array into the execute() method.  See the official PDO documentation for info
on those methods.

Output bind variables can be used too.  Just or the PDO::PARAM_INPUT_OUTPUT
flag with the variable type.

{{{#!blockquote
{{{#!code
@parts/phppdo-bind-out.php@
}}}
}}}

BLOB's output binds can be used as well.  PDO returns BLOB values as
streams, so you have to use stream functions to access them.

{{{#!blockquote
{{{#!code
@parts/phppdo-bind-lob-out.php@
}}}
}}}

PDO allows you to bind by name and by position.  So, you can pass either
a variable name or a number as the first parameter of bindParam().  When
binding by position, the first bind parameter is number 1, not 0.

A few words about the format of bind variables...

The official PDO documentation says to use ?'s in your queries when binding by position and colon-delimited names when binding by name.

However, databases differ widely in their bind variable formats and only SQLite supports both ?'s when binding by position and colon-delimited names when binding by name.

The most efficient thing to do is to ignore the PDO docs and just use whatever bind format is supported by the database you're using.

With Oracle:

{{{#!blockquote
{{{#!code
insert into testtable values (:var1,:var2,:var3,:var4)
}}}
}}}

or

{{{#!blockquote
{{{#!code
insert into testtable values (:1,:2,:3,:4)
}}}
}}}

With Sybase and MS SQL Server:

{{{#!blockquote
{{{#!code
insert into testtable values (@var1,@var2,@var3,@var4)
}}}
}}}

With DB2, MySQL and Firebird:

{{{#!blockquote
{{{#!code
insert into testtable values (?,?,?,?)
}}}
}}}

With PostgreSQL:

{{{#!blockquote
{{{#!code
insert into testtable values ($1,$2,$3,$4)
}}}
}}}

With SQLite:

{{{#!blockquote
{{{#!code
insert into testtable values (:var1,:var2,:var3,:var4)
}}}
}}}

or

{{{#!blockquote
{{{#!code
insert into testtable values (?,?,?,?)
}}}
}}}


However...

Another solution is to configure SQL Relay to translate bind variables by setting translatebindvariables="yes" in the sqlrelay.conf file for the instance you're using.  Simply put, this parameter makes every database support every bind variable format.  See the [../admin/configreference.html SQL Relay Configuration Reference] for more information on this parameter.

A third solution is to configure PDO to emulate prepares by setting the PDO::ATTR_EMULATE_PREPARES attribute to true.  When the SQL Relay driver sees that this is set true, it rewrites the queries and translates the bind variables to substitution variables.  This solution also makes every database support every bind variable format, but the translation is done on the client-side.  Unfortunately it doesn't work with output bind variables.

[=#rebinding]
== Re-Binding and Re-Execution ==

Another feature of the prepare/bind/execute paradigm is the ability to 
prepare, bind and execute a query once, then re-bind and re-execute the query 
over and over without re-preparing it.  If your backend database natively 
supports this paradigm, you can reap a substantial performance improvement.

{{{#!blockquote
{{{#!code
@parts/phppdo-reexecute.php@
}}}
}}}

[=#fields]
== Accessing Fields in the Result Set ==

To fetch a row from a statement that has been executed, call the fetch()
method.  When called with no parameters, fetch() returns an array of fields
with both numeric and associative entries.  When called with the PDO::FETCH_NUM
flag, it returns an array with only numeric entries.  When called with the
PDO::FETCH_ASSOC flag, it returns an array with only associative entries.

LOB values are returned as streams, just like output bind parameters.

Here is an example using fetch() to return both ordered and associative
arrays (the default).

{{{#!blockquote
{{{#!code
@parts/phppdo-fields.php@
}}}
}}}

The bindColumn() method and PDO::FETCH_BOUND flag can be used to fetch
values into preallocated variables.

{{{#!blockquote
{{{#!code
@parts/phppdo-fetchbound.php@
}}}
}}}

The PDO API also provides the fetchAll() convenience function for fetching
an entire result set in one step.

{{{#!blockquote
{{{#!code
@parts/phppdo-fetchall.php@
}}}
}}}

The fetchColumn() method allows you to fetch a single column of the result
set too.

{{{#!blockquote
{{{#!code
@parts/phppdo-fetchcolumn.php@
}}}
}}}

[=#largeresultsets]
== Dealing With Large Result Sets ==

SQL Relay normally buffers the entire result set.  This can speed things up 
at the cost of memory.  With large enough result sets, it makes sense to buffer the result set in chunks instead of all at once.

Use the connect string variable "resultsetbuffersize" or the statement
attribute PDO::SQLRELAY_ATTR_RESULT_SET_BUFFER_SIZE to set the number of rows
to buffer at a time.  Calls to fetch() cause the chunk containing the requested
field to be fetched.  Rows in that chunk are accessible but rows before it are
not.

For example, if you set the result set buffer size to 5 and execute a query
that returns 20 rows, rows 0-4 are available at once, then rows 5-9, then 10-14,
then 15-19.  When rows 5-9 are available, fetching row 0 will return false and
fetching row 11 will cause rows 10-14 to be fetched and return the requested
row.

The following code sets the result set buffer size to 5.

{{{#!blockquote
{{{#!code
@parts/phppdo-rsbuffersize-set.php@
}}}
}}}

The following code retrieves the result set buffer size.

{{{#!blockquote
{{{#!code
@parts/phppdo-rsbuffersize-get.php@
}}}
}}}

[=#statements]
== Statements ==

It's possible to execute new queries while processing the result
set of another query.  You can select rows from a table in one query, then 
iterate through its result set, inserting rows into another table, using only 
1 database connection for both operations.

For example:

{{{#!blockquote
{{{#!code
@parts/phppdo-statements.php@
}}}
}}}

[=#columns]
== Getting Column Information ==

After executing a query, the column count may be retrieved using
columnCount().  An associative array of column data, including column name,
type, length and various flags are available from getColumnMeta().

{{{#!blockquote
{{{#!code
@parts/phppdo-columninfo.php@
}}}
}}}


[=#storedprocedures]
== Stored Procedures ==

Many databases support stored procedures.  Stored procedures are sets of
queries and procedural code that are executed inside of the database itself.
For example, a stored procedure may select rows from one table, iterate through
the result set and, based on the values in each row, insert, update or delete
rows in other tables.  A client program could do this as well, but a stored
procedure is generally more efficient because queries and result sets don't
have to be sent back and forth between the client and database.  Also, stored
procedures are generally stored in the database in a compiled state, while
queries may have to be re-parsed and re-compiled each time they are sent.

While many databases support stored procedures.  The syntax for creating
and executing stored procedures varies greatly between databases.

Stored procedures typically take input paramters from client programs through
input bind variables and return values back to client programs either through
bind variables or result sets.  Stored procedures can be broken down into
several categories, based on the values that they return.  Some stored
procedures don't return any values, some return a single value, some return
multiple values and some return entire result sets.

=== No Values ===

Some stored procedures don't return any values.  Below are examples,
illustrating how to create, execute and drop this kind of stored procedure for
each database that SQL Relay supports.

==== Oracle ====

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/oracle-create-testproc.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-oracle-testproc.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/oracle-drop-testproc.sql@
}}}
}}}


==== Sybase and Microsoft SQL Server ====

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/sybase-create-testproc.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-sybase-testproc.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/sybase-drop-testproc.sql@
}}}
}}}

==== Firebird ====

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/firebird-create-testproc.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-firebird-testproc.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/firebird-drop-testproc.sql@
}}}
}}}

==== DB2 ====

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/db2-create-testproc.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-db2-testproc.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/db2-drop-testproc.sql@
}}}
}}}

==== Postgresql ====

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/postgresql-create-testproc.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-postgresql-testproc.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/postgresql-drop-testproc.sql@
}}}
}}}

==== !MySQL ====

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/mysql-create-testproc.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-mysql-testproc.php@
}}}
}}}

Note: Versions of !MySQL prior to 5.0 had trouble calling stored procedures using bind variables.  If you are using a version of !MySQL prior to 5.0 then SQL relay must fake the bind variables and you must use colon-delimited variables (:1, :2, :3, etc.) in your queries rather than the native-mysql queston marks.

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/mysql-drop-testproc.sql@
}}}
}}}



=== Single Values ===

Some stored procedures return single values.  Below are examples,
illustrating how to create, execute and drop this kind of stored procedure for
each database that SQL Relay supports.

==== Oracle ====

In Oracle, stored procedures can return values through output parameters or
as return values of the procedure itself.

Here is an example where the procedure itself returns a value.  Note that
Oracle calls these functions.

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/oracle-create-testproc-1value.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-oracle-testproc-1value.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/oracle-drop-testproc.sql@
}}}
}}}

Here is an example where the value is returned through an output
parameter.

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/oracle-create-testproc-1value-output-param.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-oracle-testproc-1value-output-param.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/oracle-drop-testproc.sql@
}}}
}}}


==== Sybase and Microsoft SQL Server ====

In Sybase and Microsoft SQL Server, stored procedures return values
through output parameters rather than as return values of the procedure
itself.

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/sybase-create-testproc-1value.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-sybase-testproc-1value.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/sybase-drop-testproc.sql@
}}}
}}}


==== Firebird ====

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/firebird-drop-testproc.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-firebird-testproc-1value.php@
}}}
}}}

Alternatively, you can run a query like the following and receive the result
using an output bind variable.  Note that in Firebird, input and
output bind variable indices are distict from one another.  The index of the
output bind variable is 1 rather than 4, even though there were 3 input bind
variables.

{{{#!blockquote
{{{#!code
@parts/phppdo-firebird-testproc-1value-output-param.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/firebird-drop-testproc.sql@
}}}
}}}


==== DB2 ====

In DB2, stored procedures return values through output parameters rather
than as return values of the procedure itself.

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/db2-create-testproc-1value.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-db2-testproc-1value.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/db2-drop-testproc.sql@
}}}
}}}


==== Postgresql ====

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/postgresql-create-testproc-1value.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-postgresql-testproc-1value.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/postgresql-drop-testproc-1value.sql@
}}}
}}}

==== !MySQL ====

A single value can be returned from a !MySQL function.

To create the function, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/mysql-create-testproc-1value.sql@
}}}
}}}

To execute the function from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-mysql-testproc-1value.php@
}}}
}}}

Note: Versions of !MySQL prior to 5.0 had trouble calling stored procedures using bind variables.  If you are using a version of !MySQL prior to 5.0 then SQL relay must fake the bind variables and you must use colon-delimited variables (:1, :2, :3, etc.) in your queries rather than the native-mysql queston marks.

To drop the function, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/mysql-drop-testproc.sql@
}}}
}}}

A single value can be returned in the result set of a !MySQL procedure.

To create the procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/mysql-create-testproc-1value-result-set.sql@
}}}
}}}

To execeute the procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-mysql-testproc-1value-result-set.php@
}}}
}}}

To drop the procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/mysql-drop-testproc.sql@
}}}
}}}

A single value can be returned using the output variable of a !MySQL
procedure.

To create the procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/mysql-create-testproc-1value-output-param.sql@
}}}
}}}

To execeute the procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-mysql-testproc-1value-output-param.php@
}}}
}}}

To drop the procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/mysql-drop-testproc.sql@
}}}
}}}



=== Multiple Values ===

Some stored procedures return multiple values.  Below are examples,
illustrating how to create, execute and drop this kind of stored procedure for
each database that SQL Relay supports.

==== Oracle ====

In Oracle, stored procedures can return values through output parameters or
as return values of the procedure itself.  If a procedure needs to return
multiple values, it can return one of them as the return value of the procedure
itself, but the rest must be returned through output parameters.

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/oracle-create-testproc-values.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-oracle-testproc-values.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/oracle-drop-testproc.sql@
}}}
}}}


==== Sybase and Microsoft SQL Server ====

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/sybase-create-testproc-values.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-sybase-testproc-values.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/sybase-drop-testproc.sql@
}}}
}}}


==== Firebird ====

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/firebird-create-testproc-values.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-firebird-testproc-values.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/firebird-drop-testproc.sql@
}}}
}}}


==== DB2 ====

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/db2-create-testproc-values.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-db2-testproc-values.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/db2-drop-testproc.sql@
}}}
}}}


==== Postgresql ====

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/postgresql-create-testproc-values.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-postgresql-testproc-values.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/postgresql-drop-testproc-values.sql@
}}}
}}}

==== !MySQL ====

Here's how you can get multiple values from the result
set of a !MySQL procedure.

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/mysql-create-testproc-values.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-mysql-testproc-values.php@
}}}
}}}

Note: Versions of !MySQL prior to 5.0 had trouble calling stored procedures using bind variables.  If you are using a version of !MySQL prior to 5.0 then SQL relay must fake the bind variables and you must use colon-delimited variables (:1, :2, :3, etc.) in your queries rather than the native-mysql queston marks.

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/mysql-drop-testproc.sql@
}}}
}}}

Here's how you can get multiple values from the output variables
of a !MySQL procedure.

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/mysql-create-testproc-values-output-params.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-mysql-testproc-values-output-params.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/mysql-drop-testproc.sql@
}}}
}}}



=== Result Sets ===

Some stored procedures return entire result sets.  Below are examples,
illustrating how to create, execute and drop this kind of stored procedure for
each database that SQL Relay supports.

==== Oracle ====

In Oracle, for a result set to be fetched from a stored procedure, the result set must be bound to an output parameter of type cursor. PDO had a PDO::PARAM_STMT type that could theoretically be used to get one of these but it is currently unimplemented by any driver, including the SQL Relay driver. Quirks in the implementation of PDO itself make it impossible (or at least very difficult) to implement. Specifically, there's no obvious way of creating an instance of the PDO statement type to set the bound parameter to. Hopefully this will be remedied in a future version of PDO.

==== Sybase and Microsoft SQL Server ====

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/sybase-create-testproc-result-set.sql@
}}}
}}}

To exceute the stored procedure from an SQL Relay program, ue code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-sybase-testproc-result-set.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/sybase-drop-testproc.sql@
}}}
}}}


==== Firebird ====

Stored procedures in Firebird can return a result set if a
select query in the procedure selects values into the output parameters and
then issues a suspend command, however SQL Relay doesn't currently support
stored procedures that return result sets.

==== DB2 ====

Stored procedures in DB2 can return a result set if the procedure is declared
to return one, however SQL Relay doesn't currently support stored procedures
that return result sets.

==== Postgresql ====

To create the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/postgresql-create-testproc-result-set.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-postgresql-testproc-result-set.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/postgresql-drop-testproc-result-set.sql@
}}}
}}}

==== !MySQL ====

The result sets of all select statements called within !MySQL stored
procedures (that aren't selected into variables) are returned from the procedure
call.  Though !MySQL stored procedures can return multiple result sets,
currently SQL Relay can only fetch the first result set.

To create the stored procedure which returns a result set, run a query like
the following.

{{{#!blockquote
{{{#!code
@parts/mysql-create-testproc-result-set.sql@
}}}
}}}

To execute the stored procedure from an SQL Relay program, use code like the
following.

{{{#!blockquote
{{{#!code
@parts/phppdo-mysql-testproc-result-set.php@
}}}
}}}

To drop the stored procedure, run a query like the following.

{{{#!blockquote
{{{#!code
@parts/mysql-drop-testproc.sql@
}}}
}}}


[=#lastinsertid]
== Getting the Last Insert ID ==

Databases with autoincrement or identity columns often provide functions which return the "last insert id"; the value of the autoincrement column that was generated during the insert into the database.

PDO provides the PDO::lastInsertId() method to get this value.

When using the SQLite database, you can also get the last insert id by running the query:

{{{#!blockquote
'''select last insert rowid'''
}}}

[=#driverspecific]
== Driver-Specific Attributes ==

PHP PDO allows drivers to define their own attributes for database handles and statements.  These attributes can be used with the getAttribute and setAttribute methods to control various parameters unique to the driver.  SQL Relay implements several of these.

Database handle attributes defined by the SQL Relay driver:

* '''PDO::SQLRELAY_ATTR_DB_TYPE''' - get-only, returns database type
* '''PDO::SQLRELAY_ATTR_DB_VERSION''' - get-only, returns database version string
* '''PDO::SQLRELAY_ATTR_DB_HOST_NAME''' - get-only, returns the host name of the machine the database is running on
* '''PDO::SQLRELAY_ATTR_DB_IP_ADDRESS''' - get-only, returns the ip address of the machine the database is running on
* '''PDO::SQLRELAY_ATTR_BIND_FORMAT''' - get-only, returns a string representing the bind-format accepted by the database
* '''PDO::SQLRELAY_ATTR_CURRENT_DB''' - get/set, returns or sets the current database/schema

Statement handle attributes defined by the SQL Relay driver:

* '''PDO::SQLRELAY_ATTR_RESULT_SET_BUFFER_SIZE''' - get/set, returns or sets the result set buffer size as described [#largeresultsets above].
* '''PDO::SQLRELAY_ATTR_DONT_GET_COLUMN_INFO''' - set-only (1 or 0), sets whether or not to fetch column info with the result set, default (0) is to return column info
* '''PDO::SQLRELAY_ATTR_GET_NULLS_AS_EMPTY_STRINGS''' - set-only (1 or 0), sets whether to return NULL values as empty strings or nulls, default (1) is to return NULL values as empty strings

Default values for the statement handle attributes can be set in the connect string.  Setting the corresponding connect string variable has the same effect as if the statement handle attribute were set immediately after creating the statement.  See [#session Establishing a Session] above.

[=#drivermethods]
== Driver-Specific Methods ==

PHP PDO allows drivers to define their own custom methods to provide
functionality unique to the driver.  SQL Relay implements several of these
related to ending, suspending and resuming sessions.

PDO provides a closeCursor method to close a cursor, but doesn't provide
a similar method to close a database connection, aside from reusing the
database handle or setting it to null.

Since SQL Relay can automatically re-establish closed connections when the
next query is sent, it would be nice if there was a way to release the
connection and give other clients a chance to access the database without
invalidating the database handle.  The endSession method makes this
possible.

{{{#!blockquote
{{{#!code
@parts/phppdo-endsession.php@
}}}
}}}

Since the program does some stuff that takes a long time between the second and third queries, ending the session there allows another client an opportunity to use that database connection while this client is busy. The next call to query() establishes another session. Since the program does some stuff that takes a short time between the first two queries, it's OK to leave the session open between them.

Sometimes web-based applications need a single database transaction to span
multiple pages.  Since SQL Relay sessions can be suspended and resumed, this
is possible.

First page:

{{{#!blockquote
{{{#!code
@parts/phppdo-suspend.php@
}}}
}}}

Second page:

{{{#!blockquote
{{{#!code
@parts/phppdo-resume.php@
}}}
}}}

Note the strange call of $stmt->prepare(null) above.  Resuming a result set
requires an active statment to resume the result set into.  Ideally
resumeResultSet() would be a method of the database handle and return a
statement, but the PDO developer API doesn't currently provide a method for
creating a statement programatically.  Until it does, this strangeness will be
necessary.

You can also distribute the processing of a result set across a series of
pages using suspended sessions.  If you're buffering a result set in chunks
instead of all at once and suspend a session, when you resume the session you 
can continue to retrieve rows from the result set.
