= Programming with SQL Relay using the ADO.NET API =

* [#languages Language Compatibility]
* [#session Establishing a Session]
* [#query Executing Queries]
* [#commit Commits and Rollbacks]
* [#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]
* [#cursors Cursors]
* [#columns Getting Column Information]
* [#storedprocedures Stored Procedures]
* [#extensions Extensions]
* [#lastinsertid Getting the Last Insert ID]

[=#languages]
== Language Compatibility ==

The SQL Relay ADO.NET Adapter is supported on Windows platforms using Visual Studio and on Unix/Linux using Mono.

The example code below is given in C# but the SQL Relay ADO.NET Adapter can be used from any language capable of using the .NET API.

[=#compiling]
== Compiling an SQL Relay Client Program ==

When writing an SQL Relay client program using the ADO.NET API, you need to
use the SQLRClient namespace.

{{{#!blockquote
{{{#!code
@parts/ado.net-using.cs@
}}}
}}}

You'll also need to include the SQLRelay.dll assembly.  This is usually found in C:\Program Files\Firstworks\bin on Windows or /usr/local/firstworks/lib on Unix/Linux.

As the ADO.NET Adapter ultimately relies on the C API, on Unix/Linux, there is also a SQLRelay.dll.config file, found in the same directory, that maps internal references to libsqlrclient.dll to the Unix/Linux equivalent shared object library.  This file isn't necessary on Windows and isn't installed.

To compile a progarm using the Visual Studio IDE, you just have to configure your project to include the SQLRelay.dll assembly and compile your program.

To compile from the command line using either Visual Studio or Mono, it's easiest to copy SQLRelay.dll into the current directory and build against it locally.

When using Mono, you should also copy SQLRelay.dll.config into the current directory.

For example, to create the executable sqlrexample.exe from the source code sqlrexample.cs...

From the Visual Studio command line:

{{{#!blockquote
{{{
copy "C:\Program Files\Firstworks\bin\SQLRClient.dll" .
csc /out:sqlrexample.exe sqlrexample.cs /reference:SQLRClient.dll
}}}
}}}

Using the Mono compiler from the Unix/Linux command line:

{{{#!blockquote
{{{
cp /usr/local/firstworks/lib/SQLRClient.dll .
cp /usr/local/firstworks/lib/SQLRClient.dll.config .
mcs -pkg:dotnet /out:sqlrexample.exe sqlrexample.cs /reference:SQLRClient.dll
}}}
}}}

(Note that an explicit reference to the dotnet package is required with Mono).

To run the program under Windows, you can just run it directly:

{{{#!blockquote
{{{
sqlrexample.exe
}}}
}}}

Use the mono runtime to run the program on Unix/Linux:

{{{#!blockquote
{{{
mono sqlrexample.exe
}}}
}}}

If you get an error about libsqlrclient.dll not being found, then you probably forgot to copy SQLRClient.dll.config into the current directory.

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

To use SQL Relay, you have to first open a connection.

{{{#!blockquote
{{{#!code
@parts/ado.net-session.cs@
}}}
}}}

The following connect string variables control attributes of the connection:

* '''Data Source''' - The SQL Relay server to connect to.  This may be specified as //host:port//, //host:port:socket// or just //socket//.  If host, port and socket are all three specified, then a connection will first be attempted to the local socket and then to the host/port.
* '''User ID''' - The username to use when logging into SQL Relay.
* '''Password''' - The password to use when logging into SQL Relay.
* '''Retry Time''' - If a connection fails, it will be retried on this interval (in seconds).
* '''Tries''' - If a connection fails, it will be retried this many times.
* '''Initial Catalog''' - The database/schema to switch to after logging in.  Optional.
* '''Debug''' - If this is set to true then debug is enabled.

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, User ID and Password are not typically used 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 provided in the Data Source 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 provided in the Data Source 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.

After opening  the connection, a session is established when the first 
query is run.

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 ==

There are three ways to execute queries.

If a query doesn't return a result set, such as DML (insert, update, delete, etc.) or DDL (create, drop, etc.) then you can use !ExecuteNonQuery.  If the query returns a single value you can use !ExecuteScalar.  If the query returns a result set then you must use !ExecuteReader.

In any case, you must create an instance of SQLRelayCommand and set its !CommandText.

{{{#!blockquote
{{{#!code
@parts/ado.net-execute.cs@
}}}
}}}

[=#commit]
== Commits and Rollbacks ==

If you need to execute a commit or rollback, you should create an instance of the SQLRelayTransaction class and use its commit() and rollback() methods rather than sending "commit" or "rollback" queries.  There are two reasons for this.  First, it's much more efficient to call the methods.  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() methods you instruct the database connection daemon to call the commit and rollback API methods for that database rather than issuing them as queries.  If the API's have no commit or rollback methods, the calls do nothing and the database throws no error.

{{{#!blockquote
{{{#!code
@parts/ado.net-commit-rollback.cs@
}}}
}}}

[=#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 ==

If your calls to !ExecuteNonQuery, !ExecuteScalar or !ExecuteReader fail, you can catch the error an exception.

{{{#!blockquote
{{{#!code
@parts/ado.net-errors.cs@
}}}
}}}

[=#bindvars]
== Bind Variables ==

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

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

To use bind variables (parameters) with the ADO.NET API, you must add variable/value pairs to the parameter collection associated with each instance of SQLRelayCommand.  For numeric, string or date variables, this is straightforward, you can just use the Parameters.Add() method.

{{{#!blockquote
{{{#!code
@parts/ado.net-bind.cs@
}}}
}}}

You can also bind CLOB's and BLOB's.  It is a little more complex but not terribly.

{{{#!blockquote
{{{#!code
@parts/ado.net-bind-lob.cs@
}}}
}}}

Note that in the above code, the Parameter.SQLRelayType must be set rather than Parameter.DbType, which would generally be set.  !DbType is constrained to a set of values defined in the ADO.NET spec, which does not include Clob or Blob types.

Output bind variables can be used too.  The Direction and !DbType attributes of the parameter must be specified though and for strings, the Size of the buffer to allocate for the return value must be specified as well.

{{{#!blockquote
{{{#!code
@parts/ado.net-bind-out.cs@
}}}
}}}

FIXME: ... clob, blob and cursor output bind ...

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

A 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/ado.net-reexecute.cs@
}}}
}}}

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

Once returned from an !ExecuteReader call, the SQLRelayDataReader provides methods for accessing the fields of the result set.

To fetch a row, call the Read() method.  This method will return false if no more rows are available.

After the row has been fetched, individual fields of that row may be accessed through a wide variety of methods.  The raw data for each field can be accessed by index using !GetValue(), as a numeric array returned by the !GetValues() or by using the SQLRelayDataReader directly as either a numeric or associative array.  The !GetOrdinal() method can also be used to get the numeric index of a column name as well.

Further, the raw data for each field can be coaxed into a native type using the !GetBoolean(), !GetByte(), !GetBytes(), !GetChar(), !GetChars(), !GetGuid(), !GetInt16(), !GetInt32(), !GetInt64(), !GetFloat(), !GetDouble(), !GetString(), !GetDecimal() and !GetDateTime() methods.

{{{#!blockquote
{{{#!code
@parts/ado.net-fields.cs@
}}}
}}}


[=#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 parameter SQLRelayCommand.ResultSetBufferSize to set the number of rows to buffer at a time.  Calls to SQLRelayDataReader.Read() cause the chunk containing the requested field to be fetched.

[=#cursors]
== Cursors ==

Cursors make it possible to execute 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.

In the ADO.NET API, cursors are abstracted by the SQLRelayCommand class.  Each SQLRelayCommand can be thought of as a separate cursor and in many cases, each SQLRelayCommand consumes a separate server-side cursor.

For example:

{{{#!blockquote
{{{#!code
@parts/ado.net-cursors.cs@
}}}
}}}

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

After executing a query, the column count is stored in the !FieldCount property of the SQLRelayDataReader class.  Column names are accessible via the !GetName() method.  Column types are available from the !GetDataTypeName().  The native .NET data type for the field is avialable from the !GetFieldType() method.  They may be used as follows:

{{{#!blockquote
{{{#!code
@parts/ado.net-columninfo.cs@
}}}
}}}

Extended column information is available from the !GetSchemaTable method.  Usage is best illustrated by example.

{{{#!blockquote
{{{#!code
@parts/ado.net-extended-columninfo.cs@
}}}
}}}

SQL Relay support most of the fields that might be returned in the schema table but not all.  Most databases don't support all of these fields either.  In the future, support may improve.

[=#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/ado.net-oracle-testproc.cs@
}}}
}}}

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/ado.net-sybase-testproc.cs@
}}}
}}}

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/ado.net-firebird-testproc.cs@
}}}
}}}

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/ado.net-db2-testproc.cs@
}}}
}}}

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/ado.net-postgresql-testproc.cs@
}}}
}}}

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

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

==== !MySQL/MariaDB ====

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/ado.net-mysql-testproc.sql@
}}}
}}}

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/ado.net-oracle-testproc-1value.cs@
}}}
}}}

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/ado.net-oracle-testproc-1value-output-param.cs@
}}}
}}}

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/ado.net-sybase-testproc-1value.cs@
}}}
}}}

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-1value.sql@
}}}
}}}

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

{{{#!blockquote
{{{#!code
@parts/ado.net-firebird-testproc-1value.cs@
}}}
}}}

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/ado.net-firebird-testproc-1value-output-param.cs@
}}}
}}}

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/ado.net-db2-testproc-1value.cs@
}}}
}}}

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/ado.net-postgresql-testproc-1value.cs@
}}}
}}}

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

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

==== !MySQL/MariaDB ====

A single value can be returned from a !MySQL/MariaDB 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/ado.net-mysql-testproc-1value.cs@
}}}
}}}

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/MariaDB procedure.

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

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

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

{{{#!blockquote
{{{#!code
@parts/ado.net-mysql-testproc-1value-result-set.cs@
}}}
}}}

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/MariaDB
procedure.

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

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

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

{{{#!blockquote
{{{#!code
@parts/ado.net-mysql-testproc-1value-output-param.cs@
}}}
}}}

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/ado.net-oracle-testproc-values.cs@
}}}
}}}

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/ado.net-sybase-testproc-values.cs@
}}}
}}}

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/ado.net-firebird-testproc-values.cs@
}}}
}}}

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

{{{#!blockquote
{{{#!code
@parts/ado.net-firebird-testproc-values-output-params.cs@
}}}
}}}

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/ado.net-db2-testproc-values.cs@
}}}
}}}

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/ado.net-postgresql-testproc-values.cs@
}}}
}}}

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

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

==== !MySQL/MariaDB ====

Here's how you can get multiple values from the result
set of a !MySQL/MariaDB 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/ado.net-mysql-testproc-values.cs@
}}}
}}}

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/MariaDB 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/ado.net-mysql-testproc-values-output-params.cs@
}}}
}}}

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 ====

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

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

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.  That cursor may then be opened and fetched from.  In fact, a stored procedure may return multiple result sets and each may be bound to a cursor.

ADO.NET abstracts this with the "multiple result sets" paradigm.  Output cursors must be defined when the query is executed.  Once the main query has been executed, NextResult may be called to step through the result sets bound to each cursor.

For example:

{{{#!blockquote
{{{#!code
@parts/ado.net-oracle-testproc-result-set.cs@
}}}
}}}

Note that in the above code, the Parameter.SQLRelayType must be set rather than Parameter.DbType, which would generally be set.  !DbType is constrained to a set of values defined in the ADO.NET spec, which does not include Cursor types.

This approach has some drawbacks though.  Mainly, if a stored procedure returns multiple results sets then they must be accessed sequentially.  The SQL Relay ADO.NET adapter provides another way of accessing the result sets of a stored procedure.  After execution, the Value of each cursor parameter may be cast to a SQLRelayDataReader and data may be fetched from it.  This approach allows data to be fetched from any result set, in any order.

For example:

{{{#!blockquote
{{{#!code
@parts/ado.net-oracle-testproc-result-set-bind-cursor.cs@
}}}
}}}

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

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

==== 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/ado.net-sybase-testproc-result-set.cs@
}}}
}}}

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/ado.net-postgresql-testproc-result-set.cs@
}}}
}}}

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

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

==== !MySQL/MariaDB ====

The result sets of all select statements called within !MySQL/MariaDB stored
procedures (that aren't selected into variables) are returned from the procedure
call.  Though !MySQL/MariaDB 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/ado.net-mysql-testproc-result-set.cs@
}}}
}}}

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.

Unfortunately ADO.NET doesn't expose a generic method for getting the last insert id, but when using the SQLite database, you can get the last insert id by running the query:

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