SQL Relay Configuration Guide



Quick Start

When SQL Relay is first installed, no configuration file exists. You must create one in the appropriate location. This location depends on the platform and on how you installed SQL Relay.

The most minimal sqlrelay.conf would be something like:

<?xml version="1.0"?>
<instances>

	<instance id="example">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl"/>
		</connections>
	</instance>

</instances>

This configuration defines an instance of SQL Relay named example, secured by a user and password, that opens and maintains a pool of 5 persistent connections to the orcl instance of an Oracle database using scott/tiger credentials.

The instance can be started using:

sqlr-start -id example
( NOTE: When installed from RPM packages, SQL Relay may have to be started and stopped as root.)

It can be accessed locally using:

sqlrsh -host localhost -user sqlruser -password sqlrpassword

By default, SQL Relay listens on all available network interfaces, on port 9000, and it can be accessed remotely by hostname. For example, if the server running SQL Relay is named sqlrserver then it can be accessed from another system using:

sqlrsh -host sqlrserver -user sqlruser -password sqlrpassword

The instance can be stopped using:

sqlr-stop -id example

All running instances of SQL Relay can be stopped using:

sqlr-stop

(without the -id argument)



Basic Configuration

The example above may be sufficient for many use cases, but SQL Relay has many options and for a production deployment, odds are you'll want to configure it further.

Database Connections

By default, SQL Relay opens and maintains a pool of 5 persistent database connections, but the number of connections can be configured using the connections attribute.

<?xml version="1.0"?>
<instances>

	<instance id="example" connections="10">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl"/>
		</connections>
	</instance>

</instances>

The number of connections determines how many client applications can access the database simultaneously. In this example, up to 10, assuming each client only needs one connection. Additional clients would be queued and would have to wait for one of the first 10 to disconnect before being able to access the database.

( NOTE: Any number of connections may be configured, up to an "absolute max connections" limit defined at compile-time, which defaults to 4096. To find the limit on your system, run:

sqlr-start -abs-max-connections
The command above also returns the "shmmax requirement" for the configuration. "shmmax" refers to the maximum size of a single shared memory segment, a tunable kernel parameter on most systems. The default shmmax requirement is about 5mb. On modern systems, shmmax defaults to at least 32mb, but on older systems it commonly defaulted to 512k. In any case, if the shmmax requirement exceeds the value of your system's shmmax parameter, then you will have to reconfigure the parameter before SQL Relay will start successfully. This may be done at runtime on most modern systems, but on older systems you may have to reconfigure and rebuild the kernel, and reboot.)

For Performance

In a performance-oriented configuration, a good rule of thumb is to open as many connections as you can. That number is usually environment-specific, and dictated by database, system and network resources.

Throttling

If you intend to throttle database access to a particular application, then you may intentionally configure a small number of connections.


Database Cursors

Database cursors are used to execute queries and step through result sets. Most applications only need to use one cursor at a time. Some apps require more though, either because they run nested queries, or sometimes because they just don't properly free them.

SQL Relay maintains persistent cursors as well as connections. By default, each connection opens one cursor, but the number of cursors opened by each connection can be configured using the cursors attribute.

<?xml version="1.0"?>
<instances>

	<instance id="example" connections="10" cursors="2">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl"/>
		</connections>
	</instance>

</instances>

Any number of cursors can be opened. A good rule of thumb is to open as few as possible, but as many as you know that your application will need.

( NOTE: The documentation above says that by default, each connection opens one cursor, and this is true, but it would be more accurate to say that by default each connection opens one cursor, but will open additional cursors on-demand, up to 5. This is because it is common for an app to run at least one level of nested queries. For example, it is common to run a select and then run an insert, update, or delete for each row in the result set. Unfortunately, it is also not uncommon for apps to just manage cursors poorly. So, SQL Relay's default configuration offers a bit of flexibility to accommodate these circumstances. See the next section on Dynamic Scaling for more information about configuring connections and cursors to scale up and down on-demand.)


Dynamic Scaling

Both connections and cursors can be configured to scale dynamically - open on demand and then die off when no longer needed. This feature is useful if you have spikes in traffic during certain times of day or if your application has a few modules that occasionally need more cursors than usual.

The maxconnections and maxcursors attribute define the upper bounds.

<?xml version="1.0"?>
<instances>

	<instance id="example" connections="10" maxconnections="20" cursors="2" maxcursors="10">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl"/>
		</connections>
	</instance>

</instances>

In this example, 10 connections will be started initially but more will be be started on-demand, up to 20. Each of the newly spawned connections will die off if they are inactive for longer than 1 minute.

In this example, each connection will initially open 2 cursors but more will be opened on-demand, up to 10. Each newly opened cursor will be closed as soon as it is no longer needed.

Other attributes that control dynamic scaling behavior include:

See the SQL Relay Configuration Reference for more information on these attributes.


Listener Configuration

By default, SQL Relay listens for client connections on port 9000, on all available network interfaces.

It can be configured to listen on a different port though...

<?xml version="1.0"?>
<instances>

	<instance id="example" port="9001">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl"/>
		</connections>
	</instance>

</instances>

...and accessed using:

sqlrsh -host sqlrserver -port 9001 -user sqlruser -password sqlrpassword

It can also be configured to listen on a unix socket...

<?xml version="1.0"?>
<instances>

	<instance id="example" socket="/tmp/example.socket">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl"/>
		</connections>
	</instance>

</instances>

...and accessed from the local server using:

sqlrsh -socket /tmp/example.socket -user sqlruser -password sqlrpassword

If the server has multiple network interfaces, SQL Relay can also be configured to listen on specific IP addresses.

<?xml version="1.0"?>
<instances>

	<instance id="example" addresses="192.168.1.50,192.168.1.51">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl"/>
		</connections>
	</instance>

</instances>

When configured this way, it can be accessed on 192.168.1.50 and 192.168.1.51 but not on 127.0.0.1 (localhost).

If the socket option is specified but port and addresses options are not, then SQL Relay will only listen on the socket. If addresses/port and socket options are both specified then it listens on both.


Database Configuration

By default, SQL Relay assumes that it's connecting to an Oracle database, but many other databases are supported. The dbase attribute of the instance tag specifies the database type and the connect string options (options in the string attribute of the connection tag) specify the parameters used to connect to the database. The connect string options are different for each database.

Examples follow.


Oracle

In this example, SQL Relay is configured to connect to an Oracle database. The dbase parameter defaults to "oracle", so the dbase attribute may be omitted when connecting to an Oracle database. It is just set here for illustrative purposes.

<?xml version="1.0"?>
<instances>

	<instance id="example" dbase="oracle">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=scott;password=tiger;oracle_home=/u01/app/oracle/product/12.1.0;oracle_sid=orcl"/>
		</connections>
	</instance>

</instances>

The oracle_home option refers to the base directory of an Oracle instance. On Windows platforms, it should be specified as a Windows-style path with doubled backslashes. For example:

C:\\Oracle\\ora12.1

The oracle_home option is often unnecessary though, as the $ORACLE_HOME environment variable is usually set system-wide.

The oracle_sid option refers to an entry in the tnsnames.ora file (usually $ORACLE_HOME/network/admin/tnsnames.ora) similar to:

ORCL =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = testhost)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = orcl)
    )
  )

(Note that the tnsnames.ora file must be readable by the user that the instance of SQL Relay is run as.)

If you are using Oracle Instant Client, then it's likely that you don't have an $ORACLE_HOME or a tnsnames.ora file. In that case, the oracle_sid can be set directly to a tnsnames-style expression and the oracle_home option can be omitted.

<?xml version="1.0"?>
<instances>

	<instance id="example" dbase="oracle">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=scott;password=tiger;oracle_sid=(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = testhost)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = orcl)))"/>
		</connections>
	</instance>

</instances>

See the SQL Relay Configuration Reference for other valid Oracle connect string options.

See Getting Started With Oracle for general assistance installing and configuring an Oracle database.


Microsoft SQL Server (using FreeTDS)

On Windows platforms, ODBC can be used to access a Microsoft SQL Server database. There is also a Microsoft-provided ODBC driver for some versions of Linux.

However, on Linux and Unix platforms, access to Microsoft SQL Server databases is also available using FreeTDS.

<?xml version="1.0"?>
<instances>

	<instance id="example" dbase="freetds">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="server=TESTDB;user=testuser;password=testpassword;db=testdb"/>
		</connections>
	</instance>

</instances>

The server option refers to an entry in the freetds.conf file (usually /etc/freetds.conf or /usr/local/etc/freetds.conf) which identifies the database server, similar to:

[TESTDB]
	host = testhost
	port = 1433
	tds version = 7.0
	client charset = UTF-8

(Note that the freetds.conf file must be readable by the user that the instance of SQL Relay is run as.)

See the SQL Relay Configuration Reference for other valid FreeTDS connect string options.


SAP/Sybase (using the native SAP/Sybase library)

In this example, SQL Relay is configured to connect to a SAP/Sybase database using the native SAP/Sybase library.

<?xml version="1.0"?>
<instances>

	<instance id="example" dbase="sap">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="sybase=/opt/sap;lang=en_US;server=TESTDB;user=testuser;password=testpassword;db=testdb"/>
		</connections>
	</instance>

</instances>

The sybase option refers to the base directory of the SAP/Sybase software installation. On Windows platforms, it should be specified as a Windows-style path with doubled backslashes. For example:

C:\\SAP

The sybase option is often unnecessary though, as the $SYBASE environment variable is usually set system-wide.

On Linux/Unix platforms, the server option refers to an entry in the interfaces (usually $SYBASE/interfaces) file which identifies the database server, similar to:

TESTDB
	master tcp ether testhost 5000
	query tcp ether testhost 5000

(Note that the interfaces file must be readable by the user that the instance of SQL Relay is run as.)

On Windows platforms, the server option refers to a similar entry created in an opaque location with the Open Client Directory Services Editor (dsedit).

The lang option sets the language to a value that is known to be supported by Sybase. This may not be necessary on all platforms. See the FAQ for more info.

See the SQL Relay Configuration Reference for other valid SAP/Sybase connect string options.

See Getting Started With SAP/Sybase for general assistance installing and configuring an SAP/Sybase database.


SAP/Sybase (using FreeTDS)

While the SAP/Sybase native library can be used to access a SAP/Sybase database on all platforms, on Linux and Unix platforms, access to SAP/Sybase databases is also available using FreeTDS.

<?xml version="1.0"?>
<instances>

	<instance id="example" dbase="freetds">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="server=TESTDB;user=testuser;password=testpassword;db=testdb"/>
		</connections>
	</instance>

</instances>

The server option refers to an entry in the freetds.conf file (usually /etc/freetds.conf or /usr/local/etc/freetds.conf) which identifies the database server, similar to:

[TESTDB]
	host = testhost
	port = 5000
	tds version = 5.0

(Note that the freetds.conf file must be readable by the user that the instance of SQL Relay is run as.)

See the SQL Relay Configuration Reference for other valid FreeTDS connect string options.

See Getting Started With SAP/Sybase for general assistance installing and configuring an SAP/Sybase database.


IBM DB2

In this example, SQL Relay is configured to connect to an IBM DB2 database.

<?xml version="1.0"?>
<instances>

	<instance id="example" dbase="db2">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="db=testdb;user=db2inst1;password=db2inst1pass;timeout=0"/>
		</connections>
	</instance>

</instances>

The db option refers to an entry in the local DB2 instance's database catalog. See Getting Started With IBM DB2 - Accessing Remote Instances for more information.

The timeout=0 option tells SQL Relay not to time out when connecting to the database. DB2 instances can take a long time to log in to sometimes. The default timeout is often too short.

See the SQL Relay Configuration Reference for other valid IBM DB2 connect string options.

See Getting Started With IBM DB2 for general assistance installing and configuring an IBM DB2 database.


Informix

In this example, SQL Relay is configured to connect to an Informix database.

<?xml version="1.0"?>
<instances>

	<instance id="example" dbase="informix">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="informixdir=/opt/informix;servername=ol_informix1210;db=testdb;user=testuser;password=testpassword"/>
		</connections>
	</instance>

</instances>

The informixdir option refers to the base directory of the Informix software installation. On Windows platforms, it should be specified as a Windows-style pathwith doubled backslashes. For example:

C:\\Program Files\\IBM Informix Software Bundle

The informixdir option is often unnecessary, as the $INFORMIXDIR environment variable is usually set system-wide.

On Linux and Unix platforms, the servername option refers to an entry in the sqlhosts file (usually $INFORMIXDIR/etc/sqlhosts) which identifies the database server, similar to:

ol_informix1210 onsoctcp 192.168.123.59 ol_informix1210

The second ol_informix1210 in the sqlhosts file refers to an entry in /etc/services which identifies the port that the server is listening on, similar to:

ol_informix1210 29756/tcp

(Note that the sqlhosts and /etc/services files must be readable by the user that the instance of SQL Relay is run as.)

On Windows platforms, the servername option refers to a similar entry created in an opaque location with the Setnet32 program.

See the SQL Relay Configuration Reference for other valid Informix connect string options.


MySQL

In this example, SQL Relay is configured to connect to a MySQL database.

<?xml version="1.0"?>
<instances>

	<instance id="example" dbase="mysql">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=testuser;password=testpassword;host=testhost;db=testdb"/>
		</connections>
	</instance>

</instances>

The host and db options indicate that SQL Relay should connect to the database testdb on the host testhost.

See the SQL Relay Configuration Reference for other valid MySQL connect string options.

See Getting Started With MySQL for general assistance installing and configuring a MySQL database.


PostgreSQL

In this example, SQL Relay is configured to connect to a PostgreSQL database.

<?xml version="1.0"?>
<instances>

	<instance id="example" dbase="postgresql">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=testuser;password=testpassword;host=testhost;db=testdb"/>
		</connections>
	</instance>

</instances>

The host and db options indicate that SQL Relay should connect to the database testdb on the host testhost.

See the SQL Relay Configuration Reference for other valid PostgreSQL connect string options.

See Getting Started With PostgreSQL for general assistance installing and configuring a PostgreSQL database.


Firebird

In this example, SQL Relay is configured to connect to a Firebird database.

<?xml version="1.0"?>
<instances>

	<instance id="example" dbase="firebird">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=testuser;password=testpassword;db=testhost:/opt/firebird/testdb.gdb"/>
		</connections>
	</instance>

</instances>

The db option indicates that SQL Relay should connect to the database located at /opt/firebird/testdb.gdb on the host testhost.

Note that if the database is located on a Windows host, then the path segment of the db option should be specified as a Windows-style path with doubled backslashes. For example:

testhost:C:\\Program Files\\Firebird\\Firebird_3_0\\testdb.gdb

See the SQL Relay Configuration Reference for other valid Firebird connect string options.

See Getting Started With Firebird for general assistance installing and configuring a Firebird database.


SQLite

In this example, SQL Relay is configured to connect to a SQLite database.

<?xml version="1.0"?>
<instances>

	<instance id="example" dbase="sqlite">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="db=/var/sqlite/testdb"/>
		</connections>
	</instance>

</instances>

The db option indicates that SQL Relay should connect to the database /var/sqlite/testdb.

Note that the database file (testdb in this case) and the directory that its located in (/var/sqlite in this case) must both be readable and writable by the user that the instance of SQL Relay is run as.

SQLite also supports a high-performance in-memory mode where tables are maintained in memory and nothing is written to permanent storage. To use this mode with SQL Relay, set the db option to :memory:.

As each running instance of sqlr-connection will have its own separate in-memory database, you almost certainly want to limit the number of connections to 1.

<?xml version="1.0"?>
<instances>

	<instance id="example" dbase="sqlite" connections="1">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="db=:memory:"/>
		</connections>
	</instance>

</instances>

Note that the entire in-memory database will be lost when SQL Relay is stopped. There is no way to preserve it. Such is the nature of pure in-memory databases.

See the SQL Relay Configuration Reference for other valid SQLite connect string options.

See Getting Started With SQLite for general assistance installing and configuring a SQLite database.


ODBC

ODBC can be used to access databases for which SQL Relay has no native support. On Windows platforms, ODBC is commonly used to access Microsoft SQL Server and Microsoft Access databases. On Linux platforms, the Microsoft-provided ODBC driver is also commonly used to access Microsoft SQL Server databases, as an alternative to FreeTDS.

In this example, SQL Relay is configured to connect to a ODBC database.

<?xml version="1.0"?>
<instances>

	<instance id="example" dbase="odbc">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="dsn=testdsn"/>
		</connections>
	</instance>

</instances>

The dsn option refers to an ODBC DSN (Data Source Name).

On Linux and Unix platforms, the DSN is an entry in the odbc.ini file (usually /etc/odbc.ini) which identifies the database server, similar to:

[testdsn]
Description=SQL Server
Driver=ODBC Driver 11 for SQL Server
Server=testhost
Port=1433
Database=
User=testuser
Password=testpassword

The Driver parameter refers to an entry in the odbcinst.ini file (usually /etc/odbcinst.ini) which identifies driver files:

[ODBC Driver 11 for SQL Server] 
Description=Microsoft ODBC Driver 11 for SQL Server
Driver=/opt/microsoft/msodbcsql/lib64/libmsodbcsql-11.0.so.2270.0 
Threading=1 
UsageCount=1 

(Note that the odbc.ini and odbcinst.ini files must be readable by the user that the instance of SQL Relay is run as.)

On Windows platforms, the DSN is an entry in the Windows Registry, created by the ODBC Data Source Administrator (odbcad32.exe).

See the SQL Relay Configuration Reference for other valid ODBC connect string options.

See Getting Started With ODBC (on a non-MS platform) for general assistance installing and configuring ODBC on a non-MS platform.


Microsoft Access (using MDBTools)

On Windows platforms, ODBC can be used to access a Microsoft Access database.

On Linux and Unix platforms, limited read-only access to Microsoft Access databases is available using MDBTools.

<?xml version="1.0"?>
<instances>

	<instance id="example" dbase="mdbtools">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="db=/var/mdbtools/testdb.mdb"/>
		</connections>
	</instance>

</instances>

The db option indicates that SQL Relay should connect to the database /var/mdbtools/testdb.mdb.

(Note that the database file must be readable by the user that the instance of SQL Relay is run as.)

See the SQL Relay Configuration Reference for other valid MDBTools connect string options.

See Getting Started With MS Access (on a non-MS platform) for general assistance with MS Access databases on non-MS platforms.


Multiple Instances

Any number of SQL Relay instances can be defined in the configuration file.

In following example, instances that connect to Oracle, SAP/Sybase and DB2 are defined in the same file.

<?xml version="1.0"?>
<instances>

	<instance id="oracleexample" port="9000">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl"/>
		</connections>
	</instance>

	<instance id="sapexample" dbase="sap" port="9001">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="sybase=/opt/sap;lang=en_US;server=TESTDB;user=testuser;password=testpassword;db=testdb"/>
		</connections>
	</instance>

	<instance id="db2example" dbase="db2" port="9002">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="db=testdb;user=db2inst1;password=db2inst1pass;lang=C;timeout=0"/>
		</connections>
	</instance>

</instances>

These instances can be started using:

sqlr-start -id oracleexample
sqlr-start -id sapexample
sqlr-start -id db2example
( NOTE: When installed from RPM packages, SQL Relay may have to be started and stopped as root.)

...and accessed using:

sqlrsh -host sqlrserver -port 9000
sqlrsh -host sqlrserver -port 9001
sqlrsh -host sqlrserver -port 9002

Starting Instances Automatically

In all previous examples sqlr-start has been called with the -id option, specifying which instance to start. If sqlr-start is called without the -id option then it will start all instances configured with the enabled attribute set to yes.

For example, if the following instances are defined...

<?xml version="1.0"?>
<instances>

	<instance id="oracleexample" enabled="yes" port="9000">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl"/>
		</connections>
	</instance>

	<instance id="sapexample" enabled="yes" dbase="sap" port="9001">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="sybase=/opt/sap;lang=en_US;server=TESTDB;user=testuser;password=testpassword;db=testdb"/>
		</connections>
	</instance>

	<instance id="db2example" dbase="db2" port="9002">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="db=testdb;user=db2inst1;password=db2inst1pass;lang=C;timeout=0"/>
		</connections>
	</instance>

</instances>

...then calling sqlr-start without the -id parameter will start oracleexample and sapexample because enabled="yes" is configured for those instances. db2example will not be started because enabled="yes" is not configured for that instance.

When installed on most platforms, SQL Relay creates a systemd service file (usually in /usr/lib/systemd/system or /lib/systemd/system) or an init script in the appropriate place under /etc. These call sqlr-start with no -id option. If configured to run at boot, they will start all instances for which enabled="yes" is configured.

How to enable the service file or init script depends on what platform you are using.

On systemd-enabled Linux, this usually involves running:

systemctl enable sqlrelay.service

On non-systemd-enabled Linux, Solaris, SCO and other non-BSD Unixes, this usually involves creating a symlink from /etc/init.d/sqlrelay to /etc/rc3.d/S85sqlrelay. This can be done manually, but most platforms provide utilities to do it for you.

Redhat-derived Linux distributions have a chkconfig command that can do this for you:

chkconfig --add sqlrelay

Debian-derived Linux distributions provide the update-rc.d command:

update-rc.d sqlrelay defaults

Solaris provides svcadm:

svcadm enable sqlrelay

On FreeBSD you must add a line to /etc/rc.conf like:

sqlrelay_enabled=YES

On NetBSD you must add a line to /etc/rc.conf like:

sqlrelay=YES

On OpenBSD you must add a line to /etc/rc.conf like:

sqlrelay_flags=""


Alternate Configuration File Options

Configuration Directory

While any number of SQL Relay instances can be defined in a single configuration file, it might be more convenient to split configurations up into multiple files located in a configuration directory.

The default SQL Relay configuration directory depends on the platform and on how you installed SQL Relay.

Additional configuration files may be created under this directory. These files must follow the same format as the main configuration file.

For example, if you wanted to split up oracle, sap and db2 configurations into 3 separate files, you could create:

sqlrelay.conf.d/oracle.conf

<?xml version="1.0"?>
<instances>

	<instance id="oracleexample" port="9000">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl"/>
		</connections>
	</instance>

</instances>

sqlrelay.conf.d/sap.conf

<?xml version="1.0"?>
<instances>

	<instance id="sapexample" dbase="sap" port="9001">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="sybase=/opt/sap;lang=en_US;server=TESTDB;user=testuser;password=testpassword;db=testdb"/>
		</connections>
	</instance>

</instances>

sqlrelay.conf.d/db2.conf

<?xml version="1.0"?>
<instances>

	<instance id="db2example" dbase="db2" port="9002">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="db=testdb;user=db2inst1;password=db2inst1pass;lang=C;timeout=0"/>
		</connections>
	</instance>

</instances>


Specifying Configuration Files

It is also possible to specify a particular configuration file or directory by passing the sqlr-start program a -config option.

For example:

sqlr-start -id oracleexample -config /home/myuser/sqlrelay.conf
sqlr-start -id oracleexample -config file:///home/myuser/sqlrelay.conf
sqlr-start -id oracleexample -config dir:///home/myuser/sqlrelay.conf.d
( NOTE: When installed from RPM packages, SQL Relay may have to be started and stopped as root.)

(the file:// prefix is optional when specifying a file, but the dir:// prefix must be included when specifying a directory)

The -config option may also be used to specify a comma-separated list of files or directories.

For example:

sqlr-start -id oracleexample -config /home/myuser/sqlrelay-1.conf,/home/myuser/sqlrelay-2.conf
sqlr-start -id oracleexample -config /home/myuser/sqlrelay-1.conf,/home/myuser/sqlrelay-2.conf,dir:///home/myuser/sqlrelay.conf.d
sqlr-start -id oracleexample -config /home/myuser/sqlrelay-1.conf,/home/myuser/sqlrelay-2.conf,dir:///home/myuser/sqlrelay-1.conf.d,dir:///home/myuser/sqlrelay-2.conf.d
( NOTE: When installed from RPM packages, SQL Relay may have to be started and stopped as root.)

Files and directories are processed in the order that they are specified.


Remote Configuration Files

In addition to local configuration files, the -config option may also be used to specify configuration files located on a remote host, accessible via http.

Actually, if the Rudiments library upon which SQL Relay depends was compiled with support for libcurl, then configuration files may also be remotely accessible over other protocols supported by libcurl, such as https, ftp, scp, sftp, smb, etc.

For example:

sqlr-start -id oracleexample -config http://configserver.mydomain.com/sqlrconfig/sqlrelay.conf
sqlr-start -id oracleexample -config http://myuser:mypassword@configserver.mydomain.com/sqlrconfig/sqlrelay.conf
sqlr-start -id oracleexample -config http://[/usr/local/firstworks/etc/sqlruserpwd.txt]@configserver.mydomain.com/sqlrconfig/sqlrelay.conf

sqlr-start -id oracleexample -config https://configserver.mydomain.com/sqlrconfig/sqlrelay.conf
sqlr-start -id oracleexample -config https://myuser:mypassword@configserver.mydomain.com/sqlrconfig/sqlrelay.conf
sqlr-start -id oracleexample -config https://[/usr/local/firstworks/etc/sqlruserpwd.txt]@configserver.mydomain.com/sqlrconfig/sqlrelay.conf

sqlr-start -id oracleexample -config scp://myuser:mypassword@configserver.mydomain.com/usr/local/firstworks/etc/sqlrelay.conf
sqlr-start -id oracleexample -config scp://[/usr/local/firstworks/etc/sqlruserpwd.txt]@configserver.mydomain.com/usr/local/firstworks/etc/sqlrelay.conf
( NOTE: When installed from RPM packages, SQL Relay may have to be started and stopped as root.)

( NOTE: The https and scp examples only work if Rudiments was compiled with support for libcurl.)

In some of the examples above, a user and password are given in the url, separated by a colon, prior to the @ sign. In other examples, in place of a literal user and password, a user-password file is specified in square brackets. If a user-password file is used, then the file should contain a single line, consisting of colon-separated user and password.

For example:

myuser:mypassword

Password protection is recommended for remotely accessible configuration files as they may contain users and passwords for accessing the database and SQL Relay itself.

Using user-password files is recommended over passing literal users and passwords. The files can be protected with file permissions, they prevent the user and password from being stored in the script that starts SQL Relay, and they prevent the user and password from being displayed in a process listing.


Link Files

So far, the example configuration files have all been XML files, containing configurations for instances of SQL Relay.

However, a configuration file can, alternatively, be a "link file", containing nothing but links to other configuration files.

For example:

# oracle configuration
http://myuser:mypassword@configserver.mydomain.com/sqlrconfig/oracle.conf

# sap/sybase configuration
http://myuser:mypassword@configserver.mydomain.com/sqlrconfig/sap.conf

# db2 configuration
http://myuser:mypassword@configserver.mydomain.com/sqlrconfig/db2.conf

Lines starting with # are considered to be comments and blank lines are ignored, but every other line is interpreted as the location of a local configuration file, local configuration directory, or remote configuration file, as described in the previous sections.

Each of these files or directories are processed in the order that they are specified.

Link files can be used to centralize configuration. For example, if you have several SQL Relay servers, rather than distributing configuration files across the servers, you could create an identical sqlrelay.conf file on each of them like:

http://myuser:mypassword@configserver.mydomain.com/sqlrconfig/sqlrelay.conf

And then, on configserver.mydomain.com, host an sqlrelay.conf file like:

http://myuser:mypassword@configserver.mydomain.com/sqlrconfig/oracle.conf
http://myuser:mypassword@configserver.mydomain.com/sqlrconfig/sap.conf
http://myuser:mypassword@configserver.mydomain.com/sqlrconfig/db2.conf

The files oracle.conf, sap.conf, and db2.conf could then be hosted by and maintained on that server as well.

The links in these examples are all urls, but they could just as easily be links to local files and directories as well. It is important to note though, that SQL Relay interprets all local file and directory locations relative to the local machine. If a remotely hosted link file contains a reference to a local file or directory, then SQL Relay will look for that file on the local machine, not the remote machine.

Similarly, urls are resolved using the DNS configuration of the local machine as well, not the DNS configuration of the remote machine.

The urls in these examples all contain literal users and passwords. User-password files can also be used as described in the section Remote Configuration Files. However, the user-password file must exist at the specified location on the local machine.

As link files can be protected by file permissions, and the urls stored in them aren't exposed anywhere else, such as in a process listing, user-password files are not generally necessary when using link files.

There is no limit to the depth of links. A link file can reference another link file which references another, which references another, etc. Too great a depth could lead to slow startup times though, especially when using remote configration files. This is especially significant when using Dynamic Scaling, as the configuration must be loaded each time a new connection is spawned. Care should also be taken to avoid loops.



High Availabiltiy

In a high availability environment, SQL Relay can be deployed as a front-end to provide load-balancing and fail-over for a set of replicated database servers or database cluster. Load-balancing and fail-over can also be implemented over multiple SQL Relay servers.


Load-Balancing and Fail-Over With Replicated Databases or Database Clusters

In a database cluster or replication environment, SQL Relay can be configured to maintain a pool of connections to the various database nodes and distribute client sessions over the nodes. If an individual node fails, SQL Relay will attempt to reestablish connections to that node, while continuing to distribute client sessions over the remaining nodes.

In the configuration file, each connection tag defines a node to maintain connections to. In the following example, SQL Relay is configured to distribute over three Oracle nodes - orcl1, orcl2, and orcl3

<?xml version="1.0"?>
<instances>

	<instance id="example">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl1"/>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl2"/>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl3"/>
		</connections>
	</instance>

</instances>

Any number of connection tags may be defined.

SQL Relay also supports disproportionate distribution of load. If some nodes can handle more traffic than others, then SQL Relay can be configured to send more traffic to the more capable nodes.

SQL Relay uses the connection tag's metric attribute to decide how many connections to open to each node.

<?xml version="1.0"?>
<instances>

	<instance id="example">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl1" metric="5"/>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl2" metric="15"/>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl3" metric="30"/>
		</connections>
	</instance>

</instances>

The metric attribute doesn't specify the number of connections to open to each node, but the higher the metric relative to the other metrics, the more connections to that node will be opened and maintained. For example, if the metric for the first node is twice as large as the metric for the second node, then SQL Relay will open twice as many connections to the first node as the second.

In the example above, 15 is 3 times 5, so 3 times as many connections will be opened to orcl2 as to orcl1. 30 is 6 times 5, so 6 times as many connections will be opened to orcl3 as orcl1. Since a total of 10 connections will be opened, 1 will be opened to orcl1, 3 to orcl2, and 6 to orcl2.


Already-Load-Balanced Databases

In a typical database cluster or replicated environment, the nodes are identifiable as separate hosts. However, when the nodes are located behind a load-balancing appliance or running on an application cluster, such as Oracle RAC, SQL Relay cannot identity an individual node.

In these environments, if a node goes down, SQL Relay will attempt to re-establish the connection, but rather than failing until the node comes back up, the new connection will more likely just succeed to a different node in the cluster. Over time, this can lead to disproportionate load-balancing, with a bias toward nodes that have never gone down.

SQL Relay manages this by "shuffling" the connections periodically. Every so often, each database connection is re-established, giving that connection a chance to be re-established to a node that may have gone down but is now back up.

To indicate to SQL Relay that the nodes are already-load-balanced, and need to be "shuffled" periodically, only one connection tag should be used, with the behindloadbalancer attribute set to "yes".

<?xml version="1.0"?>
<instances>

	<instance id="example">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
                        <connection string="user=scott;password=tiger;oracle_sid=orclrac" behindloadbalancer="yes"/>
		</connections>
	</instance>

</instances>


Master-Slave Query Routing

The load-balancing scenarios described above all assume that master-master replication is being used. SQL Relay supports master-slave replication as well.

In a master-slave replication environment, SQL Relay can be configured to route DML and DDL queries to the master and distribute selects over the slaves.

This actually requires 3 instances of SQL Relay. One to connect to the master, one to connect to the slaves, and a third to route queries to the first two.

<?xml version="1.0"?>
<instances>

        <!-- This instance maintains connections to the "master" MySQL database
                on the masterdb machine.  This instance only listens on the
                unix socket /tmp/master.socket and thus cannot be connected to
                by clients from another machine. -->
        <instance id="master" socket="/tmp/master.socket" dbase="mysql">
                <users>
                        <user user="masteruser" password="masterpassword"/>
                </users>
                <connections>
                        <connection string="user=masteruser;password=masterpassword;host=masterdb;db=master;"/>
                </connections>
        </instance>


        <!-- This instance maintains connections to 4 "slave" MySQL databases
                on 4 slave machines.  This instance only listens on the unix
                socket /tmp/slave.socket and thus cannot be connected to by
                clients from another machine. -->
        <instance id="slave" socket="/tmp/slave.socket" dbase="mysql">
                <users>
                        <user user="slaveuser" password="slavepassword"/>
                </users>
                <connections>
                        <connection string="user=slaveuser;password=slavepassword;host=slavedb1;db=slave;"/>
                        <connection string="user=slaveuser;password=slavepassword;host=slavedb2;db=slave;"/>
                        <connection string="user=slaveuser;password=slavepassword;host=slavedb3;db=slave;"/>
                        <connection string="user=slaveuser;password=slavepassword;host=slavedb3;db=slave;"/>
                </connections>
        </instance>


        <!-- This instance sends DML (insert,update,delete) and
                DDL (create/delete) queries to the "master" SQL Relay instance
                which, in turn, sends them to the "master" database.
                This instance sends any other queries to the "slave" SQL Relay
                instance which, in turn, distributes them over the "slave"
                databases. -->
        <instance id="router" dbase="router">
                <users>
                        <user user="routeruser" password="routerpassword"/>
                </users>
		<routers>
                        <!-- send all DML/DDL queries to "master"  -->
			<router module="regex" connectionid="master">
				<pattern pattern="^drop "/>
				<pattern pattern="^create "/>
				<pattern pattern="^insert "/>
				<pattern pattern="^update "/>
				<pattern pattern="^delete "/>
			</router>
                        <!-- send all other queries to "slave" -->
			<router module="regex" connectionid="slave">
				<pattern pattern=".*"/>
			</router>
		</routers>
		<connections>
			<connection connectionid="master" string="socket=/tmp/master.socket;user=masteruser;password=masterpassword"/>
			<connection connectionid="slave" string="socket=/tmp/slave.socket;user=slaveuser;password=slavepassword"/>
		</connections>
        </instance>

</instances>

The first two instances use familiar configuration options, but the third uses a dbtype of "router" and uses router tags to define query routing rules.

(Note the module attribute of the router tag. SQL Relay is highly modular, and many advanced features, including query routing, are implemented by loadable modules.)

(Note also the use of a notifications tag. See Notifications below for more information.)

Each router tag defines a connectionid to send the query to and a set of regular expression patterns to match. Queries that match the set of patterns defined in the pattern tags are sent to the instance of SQL Relay designated by the connectionid in the router tag.

The three instances can be started using:

sqlr-start -id master
sqlr-start -id slave
sqlr-start -id router
( NOTE: When installed from RPM packages, SQL Relay may have to be started and stopped as root.)

Client applications should connect to the router instance rather than the master or slave instances.

sqlrsh -host sqlrserver -user routeruser -password routerpassword


Front-End Load-Balancing and Fail-Over

If you are building out a high availability environment, or if your pool of application servers is just sufficiently large, you might want to set up a pool of SQL Relay servers between your application servers and the database.

SQL Relay supports two front-end load-balancing and fail-over strategies. In the first strategy, load-balancing and fail-over are provided by an appliance or application cluster. In the second, SQL Relay provides its own load-balancing and fail-over, with some help from DNS.

Multiple instances of SQL Relay can be placed behind a load-balancing appliance.

In this illustration, the load-balancing appliance is shown as a single machine, but in a true HA environment, there would be 2 or more appliances sharing a virtual IP. Alternatively, rather than using an appliance, SQL Relay can be run on an application server cluster such as Linux Virtual Server.

Round-robin DNS can be also be used to provide load-balancing and fail-over over multiple SQL Relay servers.

In a round-robin DNS scenario, multiple IP addresses are assigned to the same host name. The SQL Relay client is then configured to connect to that host. When it requests the IP addresses for the host, the client receives all of the IP addresses assigned to it, rather than just a single address.

Round-robin DNS is so-called because, traditionally, the order of the IP addresses returned on successive requests alternated reliably, in round-robin fashion. This behavior persists in many environments, but it is no longer guaranteed, as many modern DNS resolvers sort the list and return the IP addresses in the same order, every time. SQL Relay clients randomize the list though, and try to connect to each of the IP addresses, one-at-a-time, until they succeed. Doing so provides both load-balancing and fail-over without requiring an appliance or application server cluster.



Authentication/Authorization Options

There are several options for controlling which users are allowed to access an instance of SQL Relay.

User List Auth

The standard authentication/authorization option is user list auth.

When user list auth is used, a user is authenticated/authorized against a static list of valid user/password combinations. This is the method used in all of the examples above.

To enable user list auth, you must provide a list of valid user/password combinations, as follows:

<?xml version="1.0"?>
<instances>
	...
	<instance id="example" ... >
		...
		<users>
			<user user="user1" password="password1"/>
			<user user="user2" password="password2"/>
			<user user="user2" password="password2"/>
		</users>
		...
	</instance>
	...
</instances>

Database Auth

Another authentication/authorization option is database auth.

When database auth is used, a user is authenticated/authorized against the database itself. SQL Relay does this by checking the provided credentials against the credentials that are currently in use. If they differ, then it logs out of the database and logs back in using the provided credentials.

To enable database auth, omit the list of user/passwords and configure authtier="database" as follows.

<?xml version="1.0"?>
<instances>
	...
	<instance id="example" ... authtier="database" ... >
		...
	</instance>
	...
</instances>
( NOTE: Database auth should not be used in an instance where dbase="router". It's OK for the instances that the router routes to to use it, but the router instance itself should not. If database auth is used for that instance, then auth will fail.)

( NOTE: Prior to version 0.65.0, the "database" auth method defaulted to the behavior of the "proxied" auth method and fell back to the current behavior if the proxied behavior was unsupported. There was no way to force this behavior. As of 0.65.0 the funcationality is split into two separate modules.)

Proxied Auth

Another authentication/authorization option is proxied auth.

When proxied auth is used, a user is authenticated/authorized against the database itself, though in a different manner than datbase auth described above. SQL Relay logs into the database as a user with permissions to proxy other users. For each client session, SQL Relay checks the provided credentials against the credentials that are currently in use. If they differ, then it asks the proxy user to switch the user it's proxying to the provided user.

This is currently only supported with Oracle (version 8i or higher) and requires database configuration. See this document for more information including instructions for configuring Oracle.

To enable proxied auth, omit the list of user/passwords and configure authtier="proxied" as follows.

<?xml version="1.0"?>
<instances>
	...
	<instance id="example" ... authtier="proxied" ... >
		...
	</instance>
	...
</instances>
( NOTE: Proxied auth should not be used in an instance where dbase="router". It's OK for the instances that the router routes to to use it, but the router instance itself should not. If proxied auth is used for that instance, then auth will fail.)

Kerberos and Active Directory Encryption and Authentication

SQL Relay supports Kerberos encryption and authentication.

When Kerberos encryption and authentication is used:

On Linux and Unix systems, both server and client environments must be "Kerberized". On Windows systems, both server and client must join an Active Directory Domain. Note that this is only available on Professional or Server versions of Windows. Home versions cannot join Active Directory Domains.

The following configuration configures an instance of SQL Relay to use Kerberos authentication and encryption:

<?xml version="1.0"?>
<instances>

	<instance id="example" krb="yes" krbservice="sqlrelay" krbkeytab="/usr/local/firstworks/etc/sqlrelay.keytab">
		<users>
			<user user="dmuse@KRB.FIRSTWORKS.COM"/>
			<user user="kmuse@KRB.FIRSTWORKS.COM"/>
			<user user="imuse@KRB.FIRSTWORKS.COM"/>
			<user user="smuse@KRB.FIRSTWORKS.COM"/>
			<user user="FIRSTWORKS.COM\dmuse"/>
			<user user="FIRSTWORKS.COM\kmuse"/>
			<user user="FIRSTWORKS.COM\imuse"/>
			<user user="FIRSTWORKS.COM\smuse"/>
		</users>
		<connections>
			<connection string="user=scott;password=tiger;oracle_sid=orcl"/>
		</connections>
	</instance>

</instances>

Note that no passwords are required in the user list. Note also that users are specified in both user@REALM (Kerberos) format and REALM\user (Active Directory) format to support users authenticated against both systems.

To start the instance on a Linux or Unix system, you must be logged in as a user that can read the file specified by the krbkeytab parameter.

To start the instance on a Windows system, you must be logged in as a user that can proxy the service specified by the krbservice parameter (or it's default value of "sqlrelay" if omitted).

If those criteria are met, starting the Kerberized instance of SQL Relay is the same as starting any other instance:

sqlr-start -id example
( NOTE: When installed from RPM packages, SQL Relay may have to be started and stopped as root.)

To access the instance, you must acquire a Kerberos ticket-granting ticket. On a Linux or Unix system, this typically involves running kinit, though a fully Kerberized environment may acquire this during login. On a Windows system, you must log in as an Active Directory domain user.

After acquiring the ticket-granting ticket, the instance of SQL Relay may be accessed from a Linux or Unix system as follows:

sqlrsh -host sqlrserver -krb

From a Windows system, it may be necessary to specify the fully qualified Kerberos service name as well:

sqlrsh -host sqlrserver -krb -krbservice sqlrelay/sqlrserver.firstworks.com@AD.FIRSTWORKS.COM

Note the absence of user and password parameters.

Kerberos authentication establishes trust between the user who acquired the ticket-granting ticket (the user running the client program) and the service (the SQL Relay server) as follows:

Once the SQL Relay server trusts that the client is being run by the user that it says it is, the user is authorized against the list of valid users.

While Kerberos authenticated and encrypted sessions are substantially more secure than standard SQL Relay sessions, several factors contribute to a performance penalty:

Any kind of full SQL Relay session encryption should be used with caution in performance-sensitive applications.


TLS/SSL Encryption and Authentication

SQL Relay supports TLS/SSL encryption and authentication.

When TLS/SSL encryption and authentication is used:

When using TLS/SSL encryption and authentication, at minimum, a certificate must be supplied to the SQL Relay server. For highly secure production environments, this certificate should come from a trusted certificate authority. In other environments the certificate may be self-signed, or even be borrowed from another server.

The following configuration enables TLS/SSL security for an instance of SQL relay:

<?xml version="1.0"?>
<instances>

	<instance id="example" tls="yes" tlscert="/usr/local/firstworks/etc/sqlrserver.pem">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<connections>
			<connection string="user=scott;password=tiger;oracle_sid=orcl"/>
		</connections>
	</instance>

</instances>

To start the instance on a Linux or Unix system, you must be logged in as a user that can read the file specified by the tlscert parameter. If that criterium is met then the instance can be started using:

sqlr-start -id example
( NOTE: When installed from RPM packages, SQL Relay may have to be started and stopped as root.)

The instance may be accessed as follows:

sqlrsh -host sqlrserver -user sqlruser -password sqlrpassword -tls -tlsvalidate no

This establishes a TLS/SSL-encrypted session but does not validate the server's certificate or identity. The session will only continue if the server's certificate is is well-formed and hasn't expired, but the client is still vulnerable to various attacks.


For a more secure session, the client may validate that the server's certificate was signed by a trusted certificate authority, known to the system, as follows:

sqlrsh -host sqlrserver -user sqlruser -password sqlrpassword -tls -tlsvalidate ca

If the server's certificate is self-signed, then the certificate authority won't be known to the system, but it's certificate may be specified by the tlsca parameter as follows:

sqlrsh -host sqlrserver -user sqlruser -password sqlrpassword -tls -tlsvalidate ca -tlsca /usr/local/firstworks/etc/myca.pem

(.pem files are specified in this example, but on Windows systems, .pfx file or Windows Certificate Store references must be used. See The tlsca Parameter for details.)

This establishes a TLS/SSL-encrypted session with the server and validates the server's certificate, but does not validate the server's identity. The session will only continue if the server's certificate is valid, but the client is still vulnerable to various attacks.


For a more secure session, the client may validate that the host name provided by the server's certificate matches the host name that the client meant to connect to, as follows:

sqlrsh -host sqlrserver.firstworks.com -user sqlruser -password sqlrpassword -tls -tlsvalidate ca+host -tlsca /usr/local/firstworks/etc/myca.pem

(.pem files are specified in this example, but on Windows systems, .pfx file or Windows Certificate Store references must be used. See The tlsca Parameter for details.)

Note that the fully qualified host name was provided. Note also the use of the ca+host value for the tlsvalidate parameter. With these parameters, in addition to validating that the server's certificate was signed by a trusted certificate authority, the host name will also be validated. If the certificate contains Subject Alternative Names, then the host name will be compared to each of them. If no Subject Alternative Names are provided then the host name will be compared to the certificate's Common Name. The session will only continue if the sever's certificate and identity are both valid.


Unless self-signed, certificates can be expensive, so certificates are often shared by multiple servers across a domain. To manage environments like this, the host name validation can be relaxed as follows:

sqlrsh -host sqlrserver.firstworks.com -user sqlruser -password sqlrpassword -tls -tlsvalidate ca+domain -tlsca /usr/local/firstworks/etc/myca.pem

(.pem files are specified in this example, but on Windows systems, .pfx file or Windows Certificate Store references must be used. See The tlsca Parameter for details.)

Note that the fully qualified host name was provided. Note also the use of the ca+domain value for the tlsvalidate parameter. With these parameters, in addition to validating that the server's certificate was signed by a trusted certificate authority, the domain name portion of the host name will also be validated. If the certificate contains Subject Alternative Names, then the domain name portion of the host name will be compared to the domain name portion of each of them. If no Subject Alternative Names are provided then the domain name portion of the host name will be compared to the domain name portion of the certificate's Common Name. The session will only continue if the sever's certificate and domain identity are both valid.


For an even more secure session, the server may also request a certificate from the client, validate the certificate, and optionally authorize the client based on the host name provided by the certificate.

The following configuration enables these checks for an instance of SQL relay:

<?xml version="1.0"?>
<instances>

	<instance id="example"
		tls="yes"
		tlscert="/usr/local/firstworks/etc/sqlrserver.pem"
		tlsvalidate="yes"
		tlsca="/usr/local/firstworks/etc/myca.pem">
		<users>
			<user user="sqlrclient1.firstworks.com"/>
			<user user="sqlrclient2.firstworks.com"/>
			<user user="sqlrclient3.firstworks.com"/>
			<user user="sqlrclient4.firstworks.com"/>
		</users>
		<connections>
			<connection string="user=scott;password=tiger;oracle_sid=orcl"/>
		</connections>
	</instance>

</instances>

Note that no passwords are required in the user list. In this configuration, the Subject Alternative Names in the client's certificate (or Common Name of no SAN's are present) are authorized against the list of valid names.

To access the instance, the client must provide, at minimum, a certificate chain file (containing the client's certificate, private key, and signing certificates, as appropriate), as follows:

sqlrsh -host sqlrserver -tls -tlsvalidate no -tlscert /usr/local/firstworks/etc/sqlrclient.pem

(.pem files are specified in this example, but on Windows systems, .pfx file or Windows Certificate Store references must be used. See The tlscert Parameter for details.)

Note the absence of user and password parameters. Rather than passing a user and password, the client passes the specified certificate to the server. The server trusts that the client is who they say they are by virtue of having a valid certificate and the name provided by the certificate is authorized against the list of valid names.

In a more likely use case though, mutual authentication occurs - the client validates the server's certificate and the server validates the client's certificate, as follows:

sqlrsh -host sqlrserver.firstworks.com -tls -tlscert /usr/local/firstworks/etc/sqlrclient.pem -tlsvalidate ca+host -tlsca /usr/local/firstworks/etc/myca.pem

(.pem files are specified in this example, but on Windows systems, .pfx file or Windows Certificate Store references must be used. See The tlscert Parameter and The tlsca Parameter for details.)

In this example, the client provides a certificate for the server to validate, validates the host's certificate against the provided certificate authority, and validates the host's identity against the provided host name.

While TLS/SSL authenticated and encrypted sessions are substantially more secure than standard SQL Relay sessions, several factors contribute to a performance penalty:

Any kind of full SQL Relay session encryption should be used with caution in performance-sensitive applications.



Security Features

SQL Relay offers several features to enhance security.


Kerberos/Active Directory and TLS/SSL Encryption and Authentication

As mentioned in the Authentication/Authorization Options section above, SQL Relay supports Kerberos and Active Directory Encryption and Authentication and TLS/SSL Encryption and Authentication.


Run-As User and Group

When a non-root user runs sqlr-start, the SQL Relay server runs as that user and as the primary group of that user.

When root runs sqlr-start, the SQL Relay server runs as a more secure user and group, usually nobody/nobody when built from source, or sqlrelay/sqlrelay when installed from packages.

However, the runasuser and runasgroup attributes can be used to control what user and group the SQL Relay server runs as.

<?xml version="1.0"?>
<instances>

	<instance id="example" ... runasuser="exampleuser" runasgroup="examplegroup" ...>
		...
	</instance>

</instances>

There are several important considerations when setting runasuser/runasgroup:



Allowed/Denied IP Addresses

By default, clients from any IP address are allowed to connect to the SQL Relay srever. However, the deinedips and allowedips attributes can be used to restrict the set of IP addresses that clients can connect from.

The deniedips attribute can be configured with a regular expression indicating which IP address will be denied access. The allowedips attribute can also be configured with a regular expression to override the deniedips attribute.

For example, to deny all clients except clients connecting from the 192.168.2.0 and 64.45.22.0 networks:

<?xml version="1.0"?>
<instances>

	<instance id="example" ... deniedips=".*" allowedips="(192\.168\.2\..*|64\.45\.22\..*)" .../>
		...
	</instance>

</instances>


Password Encryption

Password encryption allows you to store passwords in the configuration file in a manner that makes them not directly readable. Passwords for SQL Relay users and database passwords may both be encrypted.

Encryption and decryption are achieved via loadable modules. The passwordencryptions section of the configuration file indicates which modules to load, and parameters in the user and connection tags indicate which module to use with the password defined in that same tag.

For example, to use the rot module, which encrypts by performing a simple character rotation:

<?xml version="1.0"?>
<instances>

	<instance ...>
		<passwordencryptions>
			<passwordencryption module="rot" id="rot13" count="13"/>
		</passwordencryptions>
		<users>
			<user user="test" password="grfg" passwordencryptionid="rot13"/>
		</users>
		<connections>
			<connection connectionid="db" string="user=testuser;password=grfgcnffjbeq;..." passwordencryptionid="rot13" metric="6"/>
		</connections>
	</instance>

</instances>

The module attribute specifies which module to load.

Module configurations may have attributes and/or nested tags. How these are interpreted is module-specific.

In this example, the id and count attributes are parameters for the rot module. "13" tells the module to rotate by 13 characters. The id attribute assigns this particular module configuration an id that will be referenced by user and connection tags. The id attribute is mandatory.

Note that the password in the user tag is encrypted (unencrypted, it would just be "test") and that the password in the string attribute of the connection tag is also encrypted (unencrypted, it would just be "testpassword"). A command line program (described later) is provided to encrypt passwords.

Note also that the passwordencryptionid attribute in both tags refers to the id of the module as set using the id parameter in the passwordencryption tag ( rot13 ), not the module name ( rot ).

Password encryption modules may be "stacked". It is possible to load multiple modules and use each one with a different password. For example, you might want to use the rot module with a count of 13 for the SQL Relay password and a count of 10 for the database password.

<?xml version="1.0"?>
<instances>

	<instance ...>
		<passwordencryptions>
			<passwordencryption module="rot" id="rot13" count="13"/>
			<passwordencryption module="rot" id="rot10" count="10"/>
		</passwordencryptions>
		<users>
			<user user="test" password="grfg" passwordencryptionid="rot13"/>
		</users>
		<connections>
			<connection connectionid="db" string="user=testuser;password=docdzkccgybn;..." passwordencryptionid="rot10" metric="6"/>
		</connections>
	</instance>

</instances>

Encryption modules may be either two-way or one-way. Two-way encryption modules can both encrypt and decrypt a password. One-way encryption modules can only encrypt a password.

Symmetric and asymmetric key encryption techniques are two-way. The rot encryption is an example of symmetric key encryption. Asymmetric key encryptions generally use a public/private key pair, where the publicly available key is be used to encrypt the data but a privately held key is required to decrypt it. SQL Relay can use two-way encryption modules with passwords for SQL Relay users and database passwords.

One-way encryption techniques include DES, MD5 and SHA1 hashes. When using those techniques, the password can be encrypted but cannot effectively be decrypted. SQL Relay can use one-way encryption moudles with passwords for SQL Relay users but can not use one-way encryption modules to encrypt database passwords.

The command line tool sqlr-pwdenc is provided to help encrypt passwords for inclusion in the configuration file. Given an encryption module and password, it will print out the encrypted password.

sqlr-pwdenc [-config configfile] -id id -pwdencid passwordencryptionid -password password

For example:

$ sqlr-pwdenc -id example -pwdencid rot13 -password testpassword
grfgcnffjbeq

The resulting string "grfgcnffjbeq" can now be put in the configuration file as the password.

There is one final thing to note. Command line client programs like sqlrsh and sqlr-import take a -id option. The -id option causes the program to open the configuration file and extract the host, port, socket, user and password from the specified instance. If the password is encrypted, then the encrypted password will be extracted and passed to the server. This will fail. So, when using the -id option with an encrypted password, you must also use the -user and -password option, to override the user/password that are extracted from the configuration file.

For example, rather than just using:

sqlrsh -id example

You should use:

sqlrsh -id example -user test -password test

Currently, the following password encryption modules are available in the standard SQL Relay distribution:

The rot module is a two-way encryption module that performs a character rotation, similar to the popular ROT13 algorithm, though it can rotate by any amount specified in the count attribute, not just 13 and rotates digits as well as upper and lower-case characters.

The md5 module is a one-way encryption module that encrypts the password using the MD5 algorithm.

The crypt module is a one-way encryption module that encrypts the password using the DES algorithm using a salt specified in the salt attribute. The salt is required and must be a 2 digit alphanumeric code.

Custom modules may also be developed. For more information, please contact dev@firstworks.com.


Connection Schedules

Connection schedules allow the SQL Relay server to control when users are allowed to access the database.

Connection schedules are implemented by loadable modules. The schedules section of the configuration file indicates which modules to load and what parameters to use when executing them.

<?xml version="1.0"?>
<instances>

	<instance ...>
		...
		<schedules>
			<!-- allow these users during business hours -->
			<schedule module="cron_userlist" default="deny">
				<users>
					<user user="dmuse"/>
					<user user="kmuse"/>
					<user user="imuse"/>
					<user user="smuse"/>
				</users>
				<rules>
					<allow when="* * * 2-5 8:00-11:59,13:00-16:59"/>
				</rule>
			</schedule>
		</schedules>
		...
	</instance>

</instances>

The module attribute specifies which module to load.

Module configurations may have attributes and/or nested tags. How these are interpreted is module-specific.

All schedule modules have an enabled parameter, allowing the module to be temporarily disabled. If enabled="no" is configured, then the module is disabled. If set to any other value, or omitted, then the module is enabled.

Connection schedule modules can be "stacked". Multiple different modules may be loaded, and multiple instances of the same type of module, with different configurations, may also be loaded.

<?xml version="1.0"?>
<instances>

	<instance ...>
		...
		<schedules>
			<!-- allow these users during business hours -->
			<schedule module="cron_userlist" default="deny">
				<users>
					<user user="imuse"/>
					<user user="smuse"/>
				</users>
				<rules>
					<allow when="* * * 2-5 8:00-11:59,13:00-16:59"/>
				</rule>
			</schedule>

			<!-- allow these users at any time -->
			<schedule module="cron_userlist" default="deny">
				<users>
					<user user="dmuse"/>
					<user user="kmuse"/>
				</users>
				<rules>
					<allow when="* * * * *"/>
				</rule>
			</schedule>
		</schedules>
		...
	</instance>

</instances>

At startup, the SQL Relay server creates instances of the specified schedule modules and initializes them. When a client connects, the server passes the supplied credentials to each module, in the order that they were specified in the config file. Each module applies its rules to the specified user. If a module denies access to a user then the remaining modules are ignored. If the user makes it through all modules without being denies access, then the user is allowed access.

Currently, only the ''cron_userlist'' connection schedule module is available in the standard SQL Relay distribution. Custom modules may be developed though. For more information, please contact dev@firstworks.com.

The cron_userlist module allows you to define a connection schedule for a list of users, using a cron-like syntax.

Note though, that the time-and-date fields have different meanings from traditional cron.

An example configuration follows.

<?xml version="1.0"?>
<instances>

	<instance ...>
		...
		<schedules>
			<!-- allow these users during business hours -->
			<schedule module="cron_userlist" default="deny">
				<users>
					<user user="dmuse"/>
					<user user="kmuse"/>
					<user user="imuse"/>
					<user user="smuse"/>
				</users>
				<rules>
					<allow when="* * * 2-5 8:00-11:59,13:00-16:59"/>
				</rule>
			</schedule>
		</schedules>
		...
	</instance>

</instances>

In this example, the module denies access to all users by default, but then allows access to the dmuse, kmuse, imuse, and smuse users during business hours. In this case, business hours are defined as:

Another example configuration follows.

<?xml version="1.0"?>
<instances>

	<instance ...>
		...
		<schedules>
			<!-- deny these users during non-business hours -->
			<schedule module="cron_userlist" default="allow">
				<users>
					<user user="dmuse"/>
					<user user="kmuse"/>
					<user user="imuse"/>
					<user user="smuse"/>
				</users>
				<rules>
					<deny when="* * * 2-5 00:00-7:59,12:00-12:59,17:00-23:59"/>
					<deny when="* * * 1,7 *"/>
				</rules>
			</schedule>
		</schedules>
		...
	</instance>

</instances>

In this example, the module allows access to all users by default, but then denies access to the dmuse, kmuse, imuse, and smuse users during non-business hours. In this case, non-business hours are defined as:

The users tag defines a list of users to apply the schedule to. It may contain any number of user tags.

The user tags support the following attributes:

If a user does not appear in this list then it is granted access at any time. If a user appears in the list then the schedule will be applied to that user.

The default attribute of the schedule tag defines the default rule.

The rules tag defines the list of rules that modify the default behavior. It may contain allow or deny tags.

The allow and deny tags support the following attributes:

The format of the when attribute is cron-like. There are 5 fields, separated by spaces.

Note again though, that the time-and-date fields have different meanings from traditional cron.

The fields represent, in order:

In each field, ranges may be specified with a dash, and sets may be separated by commas. A * means "all possible values".

For example:

All day, every day, at any time of day:

* * * * *

All day, every month, on the 1st, 3rd through 5th, 8th, and 10th through 12th of the month:

* * 1,3-5,8,10-12 * *

Every day from 8:00AM through 11:59AM and 1:00PM through 4:59PM:

* * * * 8:00-11:59,13:00-16:59

Every day from 1:00PM to 4:00PM:

* * * * 13:00-16:00

All day, every Saturday:

* * * 6 *

All day, every day, in February and March:

* 2,3 * * *

Every day in February and March, from noon to 3PM:

* 2,3 * * 12:00-15:00

...and so on.

In general, the module works as follows:



Query Filtering

Query Filter modules allow the SQL Relay server programs to filter out queries, and not pass them along to the database.

Query filters are implemented by loadable modules. The filters section of the configuration file indicates which filter modules to load and what parameters to use when executing them.

<?xml version="1.0"?>
<instances>
	...
	<instance ...>
		...
		<filters>
			<filter module="regex" pattern=" [0-9]*=[0-9]*" errornumbrer="100" error="regex filter violation"/>
		</filters>
		...
	</instance>
	...
</instances>

The module attribute specifies which module to load.

Module configurations may have attributes and/or nested tags. How these are interpreted is module-specific.

All filter modules have an enabled parameter, allowing the module to be temporarily disabled. If enabled="no" is configured, then the module is disabled. If set to any other value, or omitted, then the module is enabled.

All filter modules have an when parameter as well, which determines when the filter is applied. If set to "before" then the module is executed before any query translations are executed. If set to "after", or omitted, then the module is executed after all query translations have been executed. See Query Translations below for more information.

Filter modules can be "stacked". Multiple different modules may be loaded and multiple instances of the same type of module, with different configurations, may also be loaded.

<?xml version="1.0"?>
<instances>
	...
	<instance ...>
		...
		<filters>
			<filter module="regex" pattern=" [0-9]*=[0-9]*"/>
			<filter module="regex" pattern="^(create)"/>
			<filter module="regex" pattern="^(drop)"/>
			<filter module="string" pattern="hugetable" ignorecase="yes"/>
			<filter module="string" pattern="badschema" ignorecase="yes"/>
		</filters>
		...
	</instance>
	...
</instances>

At startup, the SQL Relay server creates instances of the specified filter modules and initializes them. When a query is run, the server passes the query to each module, in the order that they were specified in the config file. If a module filters out the query, then it isn't passed along to the next module, nor is it sent to the database, and the client program is told that the query failed.

When using query filters, it is helpful to use the normalize query translation too:

<?xml version="1.0"?>
<instances>
	...
	<instance ...>
		...
		<translations>
			<translation module="normalize"/>
		</translations>
		...
		<filters>
			<filter module="regex" pattern=" [0-9]*=[0-9]*"/>
		</filters>
		...
	</instance>
	...
</instances>

See Query Translations below for more information.

Currently, the following filter modules are available:

Custom modules may also be developed. For more information, please contact dev@firstworks.com.

patterns

The patterns module matches the query against a specified set of patterns. Each pattern may be a string, case-insensitive string, or regular expression. Each pattern may also be matched against the entire query, only the parts of the query that are outside of quotes, or only the parts of the query that are contained within quotes. If the query matches, then it is filtered out.

The list of patterns is given by a set of pattern child tags. Each pattern tag may contain the following attributes.

For example, with the following configuration...

<?xml version="1.0"?>
<instances>
	...
	<instance ...>
		...
		<filters>
			<filter module="patterns">
				<pattern pattern="^(drop|create)" type="regex"/>
				<pattern pattern="hugetable" type="cistring" scope="outsidequotes"/>
				<pattern pattern="badstring" scope="insidequotes" errornumbrer="100" error="pattern filter violation"/>
			</filter>
		</filters>
		...
	</instance>
	...
</instances>

These queries would be filtered out:

drop table mytable
create table mytable (col1 int)
select * from HugeTable
select * from badstringtable where col1='badstring'

But these queries would not be:

insert into mytable values (1)
select * from goodtable
select * from badstringtable where col1='goodstring'

regex

The regex module matches the query against a specified regular expression pattern. If the query matches, then it is filtered out. This module is useful if you need to do a quick match, without the complexity of the patterns module.

In addition to the module attribute, each filter tag may contain the following attributes.

For example, with the following configuration:

<?xml version="1.0"?>
<instances>
	...
	<instance ...>
		...
		<filters>
			<filter module="regex" pattern=" [0-9]*=[0-9]*" errornumbrer="100" error="regex filter violation"/>
		</filters>
		...
	</instance>
	...
</instances>

This query would be filtered out:

select * from mytable where column1=1 and 1=1

But this query would not be:

select * from mytable where column1=1

string

The string module matches the query against a specified string pattern. If the query matches, then it is filtered out. This module is useful if you need to do a quick match without the complexity of regular expressions or of the patterns module.

In addition to the module attribute, each filter tag may contain the following attributes.

For example, with the following configuration:

<?xml version="1.0"?>
<instances>
	...
	<instance ...>
		...
		<filters>
			<filter module="string" pattern="hugetable" ignorecase="yes" errornumbrer="100" error="string filter violation"/>
		</filters>
		...
	</instance>
	...
</instances>

This query would be filtered out:

select * from hugetable

But this query would not be:

select * from goodtable where column1=1


Translation

SQL Relay offers features for translating queries and result sets.


Query Translation

Query translation allows the SQL Relay server to alter queries before passing them to the database.

Query translation is implemented by loadable modules. The translations section of the configuration file indicates which translation modules to load and what parameters to use when executing them.

<?xml version="1.0"?>
<instances>
	...
	<instance id="example" ... >
		...
		<translations>
			<translation module="normalize"/>
		</translations>
		...
	</instance>
	...
</instances>

The module attribute specifies which module to load.

Module configurations may have attributes and/or nested tags. How these are interpreted is module-specific.

All translation modules have an enabled parameter, allowing the module to be temporarily disabled. If enabled="no" is configured, then the module is disabled. If set to any other value, or omitted, then the module is enabled.

At startup, the SQL Relay server creates instances of the specified translations modules and initializes them. When a query is run, the server passes the query to each module, in the order that they were specified in the config file. If a module modifies the query, then that modified query is passed on to the next module.

Currently, the following translation module is available:

Custom modules may also be developed. For more information, please contact dev@firstworks.com.

The normalize module performs the following operations on a query:

For example, the following query:

sElEcT
	*,
	'He' || 'Ll' || 'o'
from
	myTABLE
where
	myTaBLe.CoLuMn1    =     myTablE.ColuMN2  /    2

Would be translated to:

select *, 'HeLlo' from mytable where mytable.column1 = mytable.column2/2

Normalizing a query is useful when also using query filtering as it simplifies the patterns that have to be searched for.

The following parameters are currently supported:


Result Set Translation

Result set translation allows the SQL Relay server to alter fields in the result set before returning the field to the client.

Result set translation is implemented by loadable modules. The resultsettranslations section of the configuration file indicates which result set translation modules to load and what parameters to use when executing them.

<?xml version="1.0"?>
<instances>

	<instance ...>
		...
		<resultsettranslations>
			<resultsettranslation
				module="reformatdatetime"
				datetimeformat="MM/DD/YYYY HH24:MI:SS"
				dateformat="MM/DD/YYYY"
				timeformat="HH24:MI:SS"
				dateddmm="yes"
				ignorenondatetime="yes"/>
		</resultsettranslations>
		...
	</instance>

</instances>

The module attribute specifies which module to load.

Module configurations may have attributes and/or nested tags. How these are interpreted is module-specific.

All result set translation modules have an enabled parameter, allowing the module to be temporarily disabled. If enabled="no" is configured, then the module is disabled. If set to any other value, or omitted, then the module is enabled.

At startup, the SQL Relay server creates instances of the specified result set translation modules and initializes them. As each field of the result set is returned, the server passes the field to each module, in the order that they were specified in the config file. If a module modifies a field, then that modified field is passed on to the next module.

Currently, the following result set translation module is available:

Custom modules may also be developed. For more information, please contact dev@firstworks.com.

The reformatdatetime module examines the field, decides if it's a date/time field, and if so, reformats it based on the given parameters.

The following parameters are currently supported:

For example, the following configuration:

<?xml version="1.0"?>
<instances>

	<instance ...>
		...
		<resultsettranslations>
			<resultsettranslation
				module="reformatdatetime"
				datetimeformat="MM/DD/YYYY HH24:MI:SS"
				dateformat="MM/DD/YYYY"
				timeformat="HH24:MI:SS"
				dateddmm="yes"
				ignorenondatetime="yes"/>
		</resultsettranslations>
		...
	</instance>

</instances>

Would translate the following date/time field:

Jul 10 2015 05:17:55:717PM

Into:

07/10/2015 17:18:55

Note that dateddmm and dateyyddmm should usually be set to the same thing. There are very specific cases where these two parameters need to be set differently from one another. You'll know if you need to.

Note also that date/time translation in general is especially problematic with MS SQL Server. See the FAQ for more info.



Query Routing

Query routing allows the SQL Relay server to send one set of queries to one database, another set of queries to another, another set of queries to another, and so on.

To route queries, one instance of SQL Relay must be configured as a router to route queries to other instances of SQL Relay which are configured normally.

A typical use case is to configure one instance of SQL Relay to maintain connections to a master database and another instance of SQL Relay to maintain connections to a pool of slaves, then set up a third instance of SQL Relay to route queries to the other 2 instances.

This is such a common case, that it is also described above in it's own section: Master-Slave Query Routing.

There are other possiblities as well though.

The actual query routing itself is implemented by loadable modules. The routers section of the configuration file indicates which router modules to load and what parameters to use when executing them.

<?xml version="1.0"?>
<instances>

        <instance ... dbase="router" ...>
		...
		<routers>
			<router module="regex" connectionid="master">
				<pattern pattern="^drop "/>
				<pattern pattern="^create "/>
				<pattern pattern="^insert "/>
				<pattern pattern="^update "/>
				<pattern pattern="^delete "/>
			</router>
			<router module="regex" connectionid="slave">
				<pattern pattern=".*"/>
			</router>
		</routers>
		<connections>
			<connection connectionid="master" string="..."/>
			<connection connectionid="slave" string="..."/>
		</connections>
		...
        </instance>

</instances>

The module attribute specifies which module to load.

Module configurations may have attributes and/or nested tags. How these elements are interpreted is module-specific.

All router modules have an enabled parameter, allowing the module to be temporarily disabled. If enabled="no" is configured, then the module is disabled. If set to any other value, or omitted, then the module is enabled.

Router modules can be "stacked". Multiple modules may be loaded and multiple instances of the same type of module, with different configurations, may also be loaded.

In fact, the example above shows a stacked configuration. The first instance of the router module sends DDL/DML queries to a "master" database and the second instance of the router module sends all other queries to a "slave" database.

At startup, the SQL Relay server creates instances of the specified router modules and initializes them. When the client sends a query to the SQL Relay server, the server consults each router module, in the order that they were specified in the config file. Each module applies its routing rules to determine which connection to run the query on. If a module returns a connection then the remaining modules are ignored. If the query makes it through all modules without being routed to a particular connection, then the query is ignored.

Currently, the following router modules are available:

Custom modules may also be developed. For more information, please contact dev@firstworks.com.


regex

The regex module routes queries by matching them against regular expressions.

A classic Master-Slave Query Routing configuration follows.

<?xml version="1.0"?>
<instances>

        <!-- This instance maintains connections to the "master" MySQL database
                on the masterdb machine.  This instance only listens on the
                unix socket /tmp/master.socket and thus cannot be connected to
                by clients from another machine. -->
        <instance id="master" socket="/tmp/master.socket" dbase="mysql">
                <users>
                        <user user="masteruser" password="masterpassword"/>
                </users>
                <connections>
                        <connection string="user=masteruser;password=masterpassword;host=masterdb;db=master;"/>
                </connections>
        </instance>


        <!-- This instance maintains connections to 4 "slave" MySQL databases
                on 4 slave machines.  This instance only listens on the unix
                socket /tmp/slave.socket and thus cannot be connected to by
                clients from another machine. -->
        <instance id="slave" socket="/tmp/slave.socket" dbase="mysql">
                <users>
                        <user user="slaveuser" password="slavepassword"/>
                </users>
                <connections>
                        <connection string="user=slaveuser;password=slavepassword;host=slavedb1;db=slave;"/>
                        <connection string="user=slaveuser;password=slavepassword;host=slavedb2;db=slave;"/>
                        <connection string="user=slaveuser;password=slavepassword;host=slavedb3;db=slave;"/>
                        <connection string="user=slaveuser;password=slavepassword;host=slavedb3;db=slave;"/>
                </connections>
        </instance>


        <!-- This instance sends DML (insert,update,delete) and
                DDL (create/delete) queries to the "master" SQL Relay instance
                which, in turn, sends them to the "master" database.
                This instance sends any other queries to the "slave" SQL Relay
                instance which, in turn, distributes them over the "slave"
                databases. -->
        <instance id="router" dbase="router">
                <users>
                        <user user="routeruser" password="routerpassword"/>
                </users>
		<routers>
                        <!-- send all DML/DDL queries to "master"  -->
			<router module="regex" connectionid="master">
				<pattern pattern="^drop "/>
				<pattern pattern="^create "/>
				<pattern pattern="^insert "/>
				<pattern pattern="^update "/>
				<pattern pattern="^delete "/>
			</router>
                        <!-- send all other queries to "slave" -->
			<router module="regex" connectionid="slave">
				<pattern pattern=".*"/>
			</router>
		</routers>
		<connections>
			<connection connectionid="master" string="socket=/tmp/master.socket;user=masteruser;password=masterpassword"/>
			<connection connectionid="slave" string="socket=/tmp/slave.socket;user=slaveuser;password=slavepassword"/>
		</connections>
        </instance>

</instances>

In this example, 3 SQL Relay instances are defined:

In this configuration, DDL/DML queries are routed to the connectionid "master", and all other queries are routed to the connectionid "slave".

The string parameter in each connection tag provides the parameters necessary to connect to the other instances. Valid parameters include:

Note the use of a notification module to notify dba@firstworks.com if an integrity_violation event occurs. SQL Relay must maintain parallel transactions on all databases that a query may be routed to. An integrity violation occurs when a transaction control query (begin, commit, rollback, autocommit on, or autocommit off) succeeds on some of the backends but fails on others. See Notifications for information about notification modules.


Master-slave routing isn't all that the regex module can do though.

In the example below, we provide a single point of access to MySQL and PostgreSQL databases.

<?xml version="1.0"?>
<instances>

	<!-- This instance maintains connections to a MySQL database -->
	<instance id="mysqldb" port="" socket="/tmp/mysqldb.socket" dbase="mysql">
		<users>
			<user user="mysqldbuser" password="mysqldbpassword"/>
		</users>
		<connections>
			<connection string="user=mysqldbuser;password=mysqldbpassword;host=mysqldb;db=mysqldb;"/>
		</connections>
	</instance>


	<!-- This instance maintains connections to a PostgreSQL database -->
	<instance id="postgresqldb" port="" socket="/tmp/postgresqldb.socket" dbase="postgresql">
		<users>
			<user user="postgresqldbuser" password="postgresqldbpassword"/>
		</users>
		<connections>
			<connection string="user=postgresqldbuser;password=postgresqldbpassword;host=postgresqldb;db=postgresqldb;"/>
		</connections>
	</instance>


	<!-- This instance sends queries containing "mysqldb." to the mysql
		database and "postgresqldb." to the postgresql database -->
	<instance id="router" dbase="router">
		<users>
			<user user="routeruser" password="routerpassword"/>
		</users>
		<routers>
			<!-- send all mysqldb queries to "mysqldb" -->
			<router module="regex" connectionid="mysqldb">
				<pattern pattern="mysqldb\."/>
			</router>
			<!-- send all postgresqldb queries to "postgresqldb" -->
			<router module="regex" connectionid="postgresqldb">
				<pattern pattern="postgresqldb\."/>
			</router>
		</routers>
		<connections>
			<connection connectionid="mysqldb" string="socket=/tmp/mysqldb.socket;user=mysqldbuser;password=mysqldbpassword"/>
			<connection connectionid="postgresqldb" string="socket=/tmp/postgresqldb.socket;user=postgresqldbuser;password=postgresqldbpassword"/>
		</connections>
		<notifications>
			<notification module="events">
				<events>
					<event event="integrity_violation"/>
				</events>
				<recipients>
					<recipient address="dba@firstworks.com"/>
				</recipients>
			</notification>
		</notifications>
	</instance>

</instances>

In this configuration, all queries containing "mysqldb." are sent to the connectionid "mysqldb" and all queries containing "postgresqldb." are sent to the connectionid "postgresqldb".

As above, the string parameter in each connection tag provides the parameters necessary to connect to the other instances.

Note the use of a notification module, as above.


userlist

The userlist module routes queries by matching the user that ran the query against a list of users.

An example configuration follows.

<?xml version="1.0"?>
<instances>

        <!-- This instance maintains connections to an Oracle database. -->
        <instance id="oracle" socket="/tmp/oracle.socket" dbase="oracle">
                <users>
                        <user user="oracleuser" password="oraclepassword"/>
                </users>
                <connections>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl"/>
                </connections>
        </instance>


        <!-- This instance maintains connections to an SAP database. -->
        <instance id="sap" socket="/tmp/sap.socket" dbase="sap">
                <users>
                        <user user="sapuser" password="sappassword"/>
                </users>
                <connections>
                        <connection string="sybase=/opt/sap;lang=en_US;server=TESTDB;user=testuser;password=testpassword;db=testdb;"/>
                </connections>
        </instance>


        <!-- This instance sends one set of users to the Oracle database and
                all other users to the sap database. -->
        <instance id="router" dbase="router">
                <users>
                        <user user="oracleuser1" password="oraclepassword"/>
                        <user user="oracleuser2" password="oraclepassword"/>
                        <user user="oracleuser3" password="oraclepassword"/>
                        <user user="oracleuser4" password="oraclepassword"/>
                        <user user="sapuser1" password="sappassword"/>
                        <user user="sapuser2" password="sappassword"/>
                        <user user="sapuser3" password="sappassword"/>
                        <user user="sapuser4" password="sappassword"/>
                </users>
		<routers>
			<router module="userlist" connectionid="oracle">
				<user user="oracleuser1"/>
				<user user="oracleuser2"/>
				<user user="oracleuser3"/>
				<user user="oracleuser4"/>
			</router>
			<router module="userlist" connectionid="sap">
				<user user="*"/>
			</router>
		</routers>
		<connections>
			<connection connectionid="oracle" string="socket=/tmp/oracle.socket;user=oracleuser;password=oraclepassword"/>
			<connection connectionid="sap" string="socket=/tmp/sap.socket;user=sapuser;password=sappassword"/>
		</connections>
        </instance>

</instances>

In this example, 3 SQL Relay instances are defined:

In this configuration, queries made by "oracle users" are routed to the connectionid "oracle", and all other queries are routed to the connectionid "sap".

The string parameter in each connection tag provides the parameters necessary to connect to the other instances. Valid parameters include:



clientiplist

The clientiplist module routes queries by matching the client that ran the query against a list of IP addresses.

An example configuration follows.

<?xml version="1.0"?>
<instances>

        <!-- This instance maintains connections to an Oracle database. -->
        <instance id="oracle" socket="/tmp/oracle.socket" dbase="oracle">
                <users>
                        <user user="oracleuser" password="oraclepassword"/>
                </users>
                <connections>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl"/>
                </connections>
        </instance>


        <!-- This instance maintains connections to an SAP database. -->
        <instance id="sap" socket="/tmp/sap.socket" dbase="sap">
                <users>
                        <user user="sapuser" password="sappassword"/>
                </users>
                <connections>
                        <connection string="sybase=/opt/sap;lang=en_US;server=TESTDB;user=testuser;password=testpassword;db=testdb;"/>
                </connections>
        </instance>


        <!-- This instance sends one set of users to the Oracle database and
                all other users to the sap database. -->
        <instance id="router" dbase="router">
                <users>
                        <user user="routeruser" password="routerpassword"/>
                </users>
		<routers>
			<router module="clientiplist" connectionid="master">
				<client ip="192.168.*.0-50"/>
			</router>
			<router module="clientiplist" connectionid="slave">
				<client ip="*"/>
			</router>
		</routers>
		<connections>
			<connection connectionid="oracle" string="socket=/tmp/oracle.socket;user=oracleuser;password=oraclepassword"/>
			<connection connectionid="sap" string="socket=/tmp/sap.socket;user=sapuser;password=sappassword"/>
		</connections>
        </instance>

</instances>

In this example, 3 SQL Relay instances are defined:

In this configuration, queries made by users originating at IP addresses 192.168.*.0-50 are routed to the connectionid "oracle", and queries made by users originating at all other IP addresses are routed to the connectionid "sap".

Each octet of the ip parameter may be specfied as a number, a dash-separated range of numbers, or a * meaning "all possible values".

The string parameter in each connection tag provides the parameters necessary to connect to the other instances. Valid parameters include:



clientinfolist

The clientinfolist module routes queries by matching the "client info" sent by the client against a list of regular expressions. The client info can be set using the setClientInfo() method/function provided by the native SQL Relay client API. The client info cannot currently be set when using a connector for a database abstraction later such as PHP PDO or ODBC.

An example configuration follows.

<?xml version="1.0"?>
<instances>

        <!-- This instance maintains connections to an Oracle database. -->
        <instance id="oracle" socket="/tmp/oracle.socket" dbase="oracle">
                <users>
                        <user user="oracleuser" password="oraclepassword"/>
                </users>
                <connections>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl"/>
                </connections>
        </instance>


        <!-- This instance maintains connections to an SAP database. -->
        <instance id="sap" socket="/tmp/sap.socket" dbase="sap">
                <users>
                        <user user="sapuser" password="sappassword"/>
                </users>
                <connections>
                        <connection string="sybase=/opt/sap;lang=en_US;server=TESTDB;user=testuser;password=testpassword;db=testdb;"/>
                </connections>
        </instance>


        <!-- This instance sends one set of users to the Oracle database and
                all other users to the sap database. -->
        <instance id="router" dbase="router">
                <users>
                        <user user="routeruser" password="routerpassword"/>
                </users>
		<routers>
			<router module="clientinfolist" connectionid="master">
				<client ip=".*oracle.*"/>
				<client ip=".*orcl.*"/>
			</router>
			<router module="clientinfolist" connectionid="slave">
				<client ip="*"/>
			</router>
		</routers>
		<connections>
			<connection connectionid="oracle" string="socket=/tmp/oracle.socket;user=oracleuser;password=oraclepassword"/>
			<connection connectionid="sap" string="socket=/tmp/sap.socket;user=sapuser;password=sappassword"/>
		</connections>
        </instance>

</instances>

In this example, 3 SQL Relay instances are defined:

In this configuration, queries made by users who send client info which contains the string "oracle" or "orcl" to the connectionid "oracle", and queries made by users sending any other client info the connectionid "sap".

The string parameter in each connection tag provides the parameters necessary to connect to the other instances. Valid parameters include:



usedatabase

The usedatabase allows you to access databases across multiple database instances via the same SQL Relay front-end with "use database" queries.

For example, lets say you have two database instances:

A MySQL instance that hosts 3 databases:

...and a PostgreSQL instance that hosts 2 databases:

If you configure an instance of SQL Relay to access the MySQL instance, then a SQL Relay client can run queries like "use mydb1" or "use mydb2" to select the database.

Similarly, if you configure an instance of SQL Relay to access the PostgreSQL instance, then a SQL Relay client can run queries like "use pbdb1" or "use pbdb2" to select the database.

The usedatabase module allows a client connected to a single instance of SQL Relay to select the database across both instances. For example, "use mydb1" would set the current database to the mydb1 database hosted by the MySQL instance, and "use pgdb2" would set the current database to the pgdb2 database hosted by the PostgreSQL instance.

An example configuration follows.

<?xml version="1.0"?>
<instances>

	<!-- This instance maintains connections to a MySQL database -->
	<instance id="mysqldb" port="" socket="/tmp/mysqldb.socket" dbase="mysql">
		<users>
			<user user="mysqldbuser" password="mysqldbpassword"/>
		</users>
		<connections>
			<connection string="user=mysqldbuser;password=mysqldbpassword;host=mysqldb;db=mysqldb;"/>
		</connections>
	</instance>


	<!-- This instance maintains connections to a PostgreSQL database -->
	<instance id="postgresqldb" port="" socket="/tmp/postgresqldb.socket" dbase="postgresql">
		<users>
			<user user="postgresqldbuser" password="postgresqldbpassword"/>
		</users>
		<connections>
			<connection string="user=postgresqldbuser;password=postgresqldbpassword;host=postgresqldb;db=postgresqldb;"/>
		</connections>
	</instance>


	<!-- This instance sends queries to databases hosted by the mysql
		instance and postgresql instance based on "use ..." queries. -->
	<instance id="router" dbase="router">
		<users>
			<user user="routeruser" password="routerpassword"/>
		</users>
		<routers>
			<router module="usedatabase"/>
		</routers>
		<connections>
			<connection connectionid="mysqldb" string="socket=/tmp/mysqldb.socket;user=mysqldbuser;password=mysqldbpassword"/>
			<connection connectionid="postgresqldb" string="socket=/tmp/postgresqldb.socket;user=postgresqldbuser;password=postgresqldbpassword"/>
		</connections>
	</instance>

</instances>

In this example, 3 SQL Relay instances are defined:

The string parameter in each connection tag provides the parameters necessary to connect to the other instances. Valid parameters include:

When the router instance starts, it gets the list of databases available from each of the other two instances, and routes to them accordingly.

A sample sqlrsh session follows:

sqlrsh -host localhost -user routeruser -password routerpassword
sqlrsh - Version 1.1.0
        Connected to: localhost:9000 as routeruser

        type help; for help.

0> use mydb1;
0> currentdb;
mydb1
0> select * from testtable;
col1
==========================
this table is in db mydb1

        Rows Returned   : 1
        Fields Returned : 1
        Elapsed Time    : 0.001512 sec

0> use mydb2;
0> currentdb;
mydb2
0> select * from testtable;
col1
==========================
this table is in db mydb2

        Rows Returned   : 1
        Fields Returned : 1
        Elapsed Time    : 0.001512 sec

0> use pgdb1;
0> currentdb;
pgdb1
0> select * from testtable;
col1
==========================
this table is in db pgdb1

        Rows Returned   : 1
        Fields Returned : 1
        Elapsed Time    : 0.001344 sec

0>

But... What if two different instances host databases with the same name? For example, what if your MySQL instance hosts a database named db2, and your PostgreSQL instance also hosts a database named db2?

To resolve situations like this, the usedatabase module allows you to map a database to an alias. In this example, the db2 database hosted by MySQL is mapped to mydb2, and the db2 database hosted by PostgreSQL is mapped to pgdb2.

<?xml version="1.0"?>
<instances>

	... MySQL/PostgreSQL instance details omitted ...

	<instance id="router" dbase="router">
		<users>
			<user user="routeruser" password="routerpassword"/>
		</users>
		<routers>
			<router module="usedatabase">
				<map connectionid="mysqldb" db="db2" alias="mydb2"/>
				<map connectionid="postgresqldb" db="db3" alias="pgdb3"/>
			</router>
		</routers>
		<connections>
			<connection connectionid="mysqldb" string="socket=/tmp/mysqldb.socket;user=mysqldbuser;password=mysqldbpassword"/>
			<connection connectionid="postgresqldb" string="socket=/tmp/postgresqldb.socket;user=postgresqldbuser;password=postgresqldbpassword"/>
		</connections>
	</instance>

</instances>

To access the db2 database hosted by MySQL, the user would run:

use mydb2

To access the db2 database hosted by PostgreSQL, the user would run:

use pgdb2

Attempts to "use db2" would fail.


Quirks and Limitations

Query Normalization

To make pattern matching easier, SQL Relay "normalizes" the query before matching it against the pattern. The original query is run against the database but when matched against the pattern, whitespace is compresssed and the entire query (except for quoted strings) is converted to lower-case.

When matching query operators, you must use lower-cased versions of them such as "select", "insert", "and", "or", etc. When matching table names, you must use a lower-cased version of the table-name.

Perl Compatible Regular Expressions

SQL Relay is built upon the Rudiments library. Rudiments can be built with or without support for libpcre which provides support for Perl Compatible Regular Expressions. PCRE's are more powerful than standard posix regular expressions and have many more operators.

As such, if you copy a configuration file from a machine where Rudiments was compiled with PCRE support to a machine where Rudiments wasn't compiled with PCRE support, then it's possible that your patterns may not work on the new machine.

To make matters worse, sufficiently old versions of the posix regular expression functions had fewer operators than modern versions. So, even if Rudiments isn't using PCRE's, it's not impossible that after copying a configuration file from a fairly modern OS to an antique, the patterns won't work on the antique machine either.

The examples above ought to work with PCRE's and all versions of posix regular expressions.

Selects Not Showing Changes

In the scenario above where DML/DDL is sent to the master database and selects are distributed over slaves, an unintuitive thing can happen.

If you begin a transaction and do several inserts, updates and deletes, you'll find that if you do a select, you will not see your changes. This is because in a master-slave configuration, changes to the database are not pushed out to the slaves until the changes have been committed. Since your selects are being run against the slaves, you must first commit before your changes will be visible.

Stored Procedures

It's possible to use stored procedures with SQL Relay's query routing feature. However, since stored procedures are run on the database, SQL Relay can't route the individual queries run inside the stored procedure. So, the stored procedure and all queries run inside of it will be run against whichever database it was routed to.

Parallel Transactions

Router modules like userlist, clientiplist, and clientinfolist route entire sessions to one database or another. Router modules like regex route individual queries. Behind the scenes, modules which route individual queries maintain parallel transactions on each of the databases that it is routing queries to, which present the following issues.

Integrity Violations

When the client issues a begin, commit or rollback, the router issues a begin, commit or rollback to each of the databases. Similarly, if the client turns auto-commit on or off, the router turns auto-commit on or off on each of the databases.

There are scenarios where a commit, rollback or auto-commit on/off command could succeed on some of the databases and fail on others. Some databases have a 2-phase commit feature to handle these scenarios. With 2-phase commit, you can roll back a commit until you do second commit. Many databases don't support 2-phase commit though. At present, SQL Relay doesn't currently support 2-phase commit for any database. So, currently, to handle this situation, SQL Relay returns an error, disables the instance doing the query routing, and raises an integrity_violation event. If a notification is configured to notify a DBA when an integrity_violation is raised, then the DBA will receive an email about the problem. Unfortunately, there is no standard way to solve the problem. The DBA must determine the cause, resolve it manually, and restart SQL Relay.

Commits and Rollbacks

Since queries may be routed to different kinds of databases, the router has to employ some tricks to maintain parallel transactions on dissimilar databases. Some databases run in auto-commit mode by default and must be issued a "begin" query to start a transaction. Other databases implicitly start a new transaction when a client logs in and after each commit or rollback. If any of the databases being routed to require a "begin" query to start a transaction, then the ones that don't are put in auto-commit mode when the client logs in and after each commit or rollback and are taken out of auto-commit mode when the client sends a begin query. If none of the databases being routed to require a "begin" query to start a transaction, then the databases are not put in auto-commit mode when the client logs in or after each commit or rollback. Rather, transactions are implicitly started by the database. For example, if your client application is using a router which routes queries over both PostgreSQL and Oracle databases, then since PostgreSQL requires "begin" queries, you must use a "begin" query to start a transaction, even if your app only intends to send queries which would be run against Oracle. Conversely, if your client application is using a router which only routes queries over a set of Oracle databases, then you do not have to use "begin" queries.



Logging

Logging allows the SQL Relay server programs to log various bits of information as they run.

Logging is implemented by loadable modules. The loggers section of the configuration file indicates which modules to load and what parameters to use when executing them.

<?xml version="1.0"?>
<instances>
 
	<instance ...>
		...
		<loggers>
			<logger module="debug" listener="yes" connection="yes"/>
		</loggers>
		...
	</instance>

</instances>

The module attribute specifies which module to load.

Module configurations may have attributes and/or nested tags. How these are interpreted is module-specific.

Different modules may have different parameters. In this example, listener="yes" tells the module to log debug info for the sqlr-listener processes and connection="yes" tells the module to log debug info for the sqlr-connection processes.

All logger modules have an enabled parameter, allowing the module to be temporarily disabled. If enabled="no" is configured, then the module is disabled. If set to any other value, or omitted, then the module is enabled.

Logger modules can be "stacked". Multiple different modules may be loaded and multiple instances of the same type of module, with different configurations, may also be loaded.

At startup, the SQL Relay server processes create instances of the specified logger modules and initialize them. As events occur, the server passes the event, log level, and optionally, a string of information about the event to each module, in the order that they were specified in the config file. If a module is listening for that event, at that log level, then it logs information about the event to a log file.

Currently, the following standard logger modules are available:

Custom modules may also be developed. For more information, please contact dev@firstworks.com.

debug

The debug module logs a great deal of information to about the internal operation of the SQL Relay server to log files in the "debug directory", usually /usr/local/firstworks/var/log/sqlrelay/debug or /usr/local/firstworks/var/sqlrelay/debug. It creates files named sqlr-listener."pid" and sqlr-connection."pid" where "pid" is replaced with the process id of the process that is being logged. As new processes are forked, new files are created with debug information about those processes.

This module takes three parameters: listener, connection and perms. The listener parameter may be set to "no" to disable logging of the sqlr-listener processes. The connection parameter may be set to "no" to disable logging of the sqlr-connection processes. Logging is enabled if either parameter is omitted or set to any other value. The perms parameter may be set to any ls -l style permissions string. The default is "rw-------" which translates to read/write for owner only.

The general log format is:

mm/dd/yyyy hh:mm:ss TZ processname [pid] : info

Sample log for main listener process: sqlr-listener.1869
Sample log for child listener process: sqlr-listener.1886
Sample log for connecton process: sqlr-connection.1871

slowqueries

The slowqueries module logs queries that take longer to run than a specified threshold to log files in the "log directory", usually /usr/local/firstworks/var/log/sqlrelay or /usr/local/firstworks/var/sqlrelay/log. It creates files named sqlr-connection-"id"-querylog."pid" for each sqlr-connection process where "id" is replaced with the id of the instance from the configuration file and "pid" is replaced with the process id.

This module takes two parameters: sec and usec. Queries that take longer than sec seconds and usec microseconds will be logged. Both parameters default to 0 and omitting them causes all queries to be logged.

The general format is:

Mon 2014 Apr  6 15:58:17 :
select 1 from dual
time: 0.000001

Sample log: sqlr-connection-oracletest-querylog.2899



Notifications

Notifications allow the SQL Relay server programs to notify recipients when a specified set of events occur.

Notifications are implemented by loadable modules. The notifications section of the configuration file indicates which notification modules to load and what parameters to use when executing them.

<?xml version="1.0"?>
<instances>

	<instance ...>
		...
		<notifications>
			<notification module="events">
				<events>
					<event event="db_error"/>
					<event event="db_warning"/>
					<event event="filter_violation"/>
				</events>
				<recipients>
					<recipient address="dev@firstworks.com"/>
				</recipients>
			</notification>
		</notifications>
		...
	</instance>

</instances>

The module attribute specifies which module to load.

Module configurations may have attributes and/or nested tags. How these are interpreted is module-specific.

All notification modules have an enabled parameter, allowing the module to be temporarily disabled. If enabled="no" is configured, then the module is disabled. If set to any other value, or omitted, then the module is enabled.

Notification modules can be "stacked". Multiple different modules may be loaded and multiple instances of the same type of module, with different configurations, may also be loaded.

<?xml version="1.0"?>
<instances>

	<instance ...>
		...
		<notifications>
			<notification module="events">
				<events>
					<event event="db_error"/>
					<event event="db_warning"/>
					<event event="filter_violation"/>
				</events>
				<recipients>
					<recipient address="dev@firstworks.com"/>
				</recipients>
			</notification>
			<notification module="events">
				<events>
					<event event="integrity_violation"/>
				</events>
				<recipients>
					<recipient address="dba@firstworks.com"/>
				</recipients>
			</notification>
		</notifications>
		...
	</instance>

</instances>

At startup, the SQL Relay server processes create instances of the specified notification modules and initializes them. As events occur, the server passes the event and, optionally, a string of information about the event to each module, in the order that they were specified in the config file. If a module is listening for that event, then it sends a notification to the specified recpients.

Currently, the following standard notification module is available:

Custom modules may also be developed. For more information, please contact dev@firstworks.com.

The events module listens for a specified set of events and notifies recipients when a one occurs.

An example configuration follows.

<?xml version="1.0"?>
<instances>

	<instance ...>
		...
		<notifications>
			<notification module="events">
				<events>
					<event event="db_error"/>
					<event event="db_warning"/>
					<event event="filter_violation"/>
				</events>
				<recipients>
					<recipient address="dev@firstworks.com"/>
				</recipients>
			</notification>
		</notifications>
		...
	</instance>

</instances>

In this example, the module sends notifications to dev@firstworks.com when one of the db_error, db_warning, or filter_violation events occurs.

The events tag defines the set of events to listen for. Valid events are:

Any number of events may be specified. Each event must be specified in its own event tag. The event tag supports the following attributes:

Any number of recipients may also be specified. Each recipient must be specified in its own recipient tag. The recipient tag supports the following attributes:

Currently, notifications may only be sent via email.

If the subject attribute is not provided, then SQL Relay uses a default subject of:

SQL Relay Notification: @event@

Where @event@ is replaced with the event that triggered the notification.

If the template attribute is not provided, then SQL Relay uses a default template of:

SQL Relay Notification:

Event          : @event@
Event Info     : @eventinfo@
Date           : @datetime@
Host Name      : @hostname@
Instance       : @instance@
Process Id     : @pid@
Client Address : @clientaddr@
Client Info    : @clientinfo@
User           : @user@
Query          :
@query@

In both subject lines and template files, the following substitutions can be made:

On linux/unix systems, the mail program is used to send notifications. Messages are sent using the following command:

mail -s subject address < message

Where subject is replaced with the subject, address is replaced with the recipient and messagee is replaced with the name of the temporary file that is used to store the message.

SQL Relay assumes that the mail program is installed, in the PATH of the user that SQL Relay runs as, and that mail delivery is configured on the host system.

On Windows systems the blat program is used to send notifications. Messages are sent using the following command:

blat message -to address -subject subject -q

Where subject is replaced with the subject, address is replaced with the recipient and message is replaced with the name of the temporary file that is used to store the message.

SQL Relay assumes that the blat program is installed, in the PATH of the user that SQL Relay runs as, and that blat has been configured. See http://www.blat.net to download and configure blat.



Session-Queries

SQL Relay can be configured to run a set of queries at the beginning and end of each client session.

By far the most common use for this feature is that some database parameter needs to be reconfigured but you don't have permission or bouncing the database is out of the question, or something like that. For example, lets say you are using an Oracle database, but your app requires dates to be formatted like MM/DD/YYYY instead of DD-MON-YYYY. Ideally you'd alter the nls_date_format in the instance but you can't, for some reason.

You can use SQL Relay's session queries to work around the problem.

In the following example, the date format is set to MM/DD/YYYY at the beginning of the session and then reset back to DD-MON-YYYY at the end.

<?xml version="1.0"?>
<instances>

	<instance id="example">
		<users>
			<user user="sqlruser" password="sqlrpassword"/>
		</users>
		<session>
			<start>
				<runquery>alter session set nls_date_format='MM/DD/YYYY'</runquery>
			</start>
			<end>
				<runquery>alter session set nls_date_format='DD-MON-YYYY'</runquery>
			</end>
		</session>
		<connections>
                        <connection string="user=scott;password=tiger;oracle_sid=orcl"/>
		</connections>
	</instance>

</instances>

Actually, in this example, there's no need to set the date format back to DD-MON-YYYY but it's done here for illustrative purposes.



Advanced Configuration

The configuration file supports many more attributes and features than the ones described in this guide including tuning options. See the SQL Relay Configuration Reference and Tuning SQL Relay for more information.