This handler reads data from a database suing an ODBC driver. There are many different database tools that support access using ODBC.
In addition to the Hyrax server, you will need a database that can be accessed using ODBC and an ODBC driver for that database that is compatible with the unixODBC software package. This includes most Relational Database Management Systems (RDBMS) as well as many other interesting tools, including NoSQL databases like MongoDB.
If your computer does not have the unixODBC driver software installed, see 'Installing ODBC on your Hyrax server' below. To build the Hyrax sql handler, you only need the generic components of unixODBC to be installed. However, to actually serve data from a database, you will need the appropriate (database specific) driver.
Unlike most of the BES modules, this handler for SQL data is a separate project. It can be added to the bes/modules directory or built outside of the bes software.
To build the code:
autoreconf -fiv./configure --prefix=$prefixmakemake check(See more below)make install
Build Notes
- The
./configurescript must find both libdap and the BES. It will use the pkgconfig package manager and, if that fails, look for dap-config and bes-config on the PATH. configure or make doesn't work - This will happen if unixODBC is not installed. Only the generic part of unixODBC is needed to configure and build the handler. We suggest you use a binary package from yum, apt-get, or brew (for CentOS, Ubuntu or OSX, respectively). make check doesn't work
- In order to run
make checkyou will need to install the test database located indata- Install DBMS as needed (See Installing DBMS software on your server below).
- Load database tables in data into DBMS? (See Creating the test database below)
- how to configure odbc.ini etc.
The sql_handler (and by extension Hyrax) uses
Open Database Connectivity (ODBC)
drivers to access various DBMS systems. Drivers will need to be installed on the Hyrax host system.
unixODBC - This open source ODBC driver is available:
- As a source distribution (utilizes gnu autotools to build)
- In the
yuminventory for CentOS-6 and CentOS-7 and - In the
homebrewinventory for OSX
Some DBMS producers provide their own ODBC drivers. See the DBMS sections below.
In order to serve data from a database you need to have a database running to hold the data and respond to queries. If you haven't got that sorted already, here's some info about how to go about it. In the following, we describe how to configure sqlite for use with ODBC drivers. This is overkill in many ways, but it is an almost universal example. In the appendix we include information about PostGreSQL and MySQL.
For this example, we assume you don't have the database installed. Folling these steps you should be able to get a working example.
Install SQLite if needed
The SQLite binaries are described as:
A bundle of command-line tools for managing SQLite database files, including the command-line shell program, the sqldiff program, and the sqlite3_analyzer program.
Linux
- Latest Binaries (Current version 3.28.0)
- Also available via: yum (Current available version on CentOS-7 is 3.7.17)
OSX
- Latest Binaries
- Also available via: Homebrew
- Use
brew install sqlitebut note that OSX already has sqlite3, which is an older version of the code, so you need to modify the PATH environment variable like this:export PATH="/usr/local/opt/sqlite/bin:$PATH"and probably put that into.bashrcas well. - Install the sqliteodbc drivers using
brew install sqliteodbc
There are some example odbc.ini and odbcinst.ini files in the install.dir
directory, including ones for sqlite's ODBC driver. These two files configure
the unixODBC driver.
- See Command Line Shell For SQLite for help with the sqlite command shell.
odbcinst.ini- Defines driver options; it creates an association between an odbc driver and a dbms. Location:/etc/odbcinst.inior/usr/local/etc/odbcinst.ini- See the odbcinst tool for an alternative way to provide this information (i.e., no editing a config file).
odbc.ini- Defines connection options; it creates view into the database and exposes it via the ODBC interface. Location:/etc/odbc.ini,/usr/local/etc/odbc.ini, or$HOME/.odbc.ini
The sqlite_odbcinst.ini should be renamed to odbcinst.ini and copied to /etc
or /usr/local/etc. It holds the following:
[SQLite]
Description=SQLite ODBC Driver
Driver=/usr/local/lib/libsqlite3odbc.so
Setup=/usr/local/lib/libsqlite3odbc.so
Threading=2Note that one odbcinst.ini can hold information for several database/driver combinations.
Separate different sections of the file for different databases using []
The sqlite_odbc.ini should be renamed to odbc.ini and also copied to /etc or /usr/local/etc.
[sqlite_test]
Description=My SQLite test database
Driver=SQLite
Database=/Users/jimg/src/opendap/hyrax_git/bes/modules/sql_handler/tests/sqlite_test.db
# optional lock timeout in milliseconds
Timeout=2000-
The code includes the
sqlite_test.dbin thesql_handler/tests/directory. This database is used for the sqlite-based tests. -
There is a SQL script in
install.dirnamedsqlh.sqlthat can build the needed database if needed. -
The name used in
odbc.ini(e.g.,[sqlite_test]above) is used in a 'dataset' file that enables much of SQL to be used when connecting one or more tables from the database to Hyrax via the sql_handler. -
Dataset files show up as 'data files' and can be browsed and accessed just like any other OPeNDAP data set.
-
There are sample 'dataset' files in the sql_hanlder/data directory. Each ends in the extension
.sqleven though they are not SQL program files. -
You can change the extensions used by the handler by editing the TypeMatch regular expression in the
sql.conffile used to configure the handler. -
See isql for a command line tool to help debug the ODBC driver configuration. For the test database,
isql sqlite_testshould return the following:
+---------------------------------------+
| Connected! |
| |
| sql-statement |
| help [tablename] |
| quit |
| |
+---------------------------------------+
SQL> You can enter commands at teh SQL> prompt. The command help sqlh_table should print
information about the table in the test sqlite database.
This is one of the sample 'dataset' files (data/sqlite.sql):
# Test variable definitions
# Set DSN to the name in square brackets in odbc.init.
# For the sqlite example bundled with the handler, that is [sqlite_test].
define $DSN$=sqlite_test
[section]
api=odbc
server=$DSN$
# The user and pass keywords are not needed
# user=root
# pass=opendap
[select]
a, b, c
[from]
sqlh_tableLinux
- All Versions
- Also available via yum (Current available version on AWS CentOS-7 is 9.2.24)
OSX
- All Versions (Package Installer)
- Also available via: Homebrew (Currently 11.2)
- There is also a PostGres Menubar App for OSX that creates menubar controls for the PostGres DBMS (conflicts with the package installer version of the DB)
PostGreSQL ODBC Implementation - All Versions Caveat Emptor: It would appear that this archive contains Windows binaries and source code for the rest (Linuix etc.).
Linux
Available as a generic binary download.
Not currently available via yum on AWS CentOS-7
OSX
Available as either as either a package installer or tar archive download.
Also available via: Homebrew (Currently: 8.0.15)
These examples include building a database that can be used with the tests.
Following one of these will result in a configuration such that make check
should work.
- Installed using the OSX package installer located here
- If installing using homebrew on OSX, the rest of the Postgresql instructions worked but manually adding the 'postgres' user was needed. To do this use the command:
/usr/local/opt/postgres/bin/createuser -s postgresWARNING: The PostGres Menubar App for OSX will not work with the software installed by the package installer.
- Created
testdatabase using the pgAdmin application. (orpg_ctl -D /usr/local/var/postgres startand thencreatedb testfor homebrew installs) - Opened the SQL Shell (
psql) application and connected to thetestdatabase. - Using the cut buffer I copied all but the first line (which selects which database to connect to, and accomplished
in the previous step) from the
sglh.sqlfile, and pasted those lines into thepsqlshell. This successfully created the table and values for the tests.
CREATE TABLE sqlh_table
(a integer NOT NULL DEFAULT 0,
b real NOT NULL DEFAULT 0,
c varchar(50),
CONSTRAINT pk PRIMARY KEY (a));
INSERT INTO sqlh_table(a, b, c) VALUES ('1', '81.0', 'string_a'), ('2', '61.1', 'string_b'), ('3', '51.0', 'string_c'), ('4', '2100.0', 'string_d'), ('5', '21.0', 'string_e'), ('6', '4133.0', 'string_f'), ('7', '31.4', 'string_g'), ('8', '21.3', 'string_h'), ('9', '11.6', 'string_i'), ('10', '22.2', 'string_j');Installed unixODBC
I used Homebrew to install unixODBC:
brew install unixodbc
- Downloaded the PostGreSQL ODBC Implementation source bundle.
- Followed the instructions here
./configure
make
make installThe ODBC configuration is held in two files:
/usr/local/etc/odbcinst.iniwhich defines the dbms connection/usr/local/etc/odbc.iniwhich defines the view of a database to the system.
Example /usr/local/etc/odbcinst.ini:
[PostgreSQL]
Description=PostgreSQL ODBC driver
Driver=/usr/local/lib/psqlodbca.so
debug=0
Threading=2
UsageCount=1Example /usr/local/etc/odbc.ini:
[test]
Driver = PostgreSQL
Description = LocalPostgres
Trace = Yes
TraceFile = sql.log
Database = test
Servername = localhost
UserName = postgres
Password = foobar
Port = 5432
Protocol = 8.4
ReadOnly = No
RowVersioning = No
ShowSystemTables = No
ShowOidColumn = No
FakeOidIndex = No
ConnSettings =At this point I was able to get the isql application to run and return the correct data:
[-bash: ~/OPeNDAP/hyrax/sql_handler] isql -v test
+---------------------------------------+
| Connected! |
| |
| sql-statement |
| help [tablename] |
| quit |
| |
+---------------------------------------+
SQL> SELECT a,b,c FROM sqlh_table;
+------------+----------------+---------------------------------------------------+
| a | b | c |
+------------+----------------+---------------------------------------------------+
| 1 | 81 | string_a |
| 2 | 61.099998 | string_b |
| 3 | 51 | string_c |
| 4 | 2100 | string_d |
| 5 | 21 | string_e |
| 6 | 4133 | string_f |
| 7 | 31.4 | string_g |
| 8 | 21.299999 | string_h |
| 9 | 11.6 | string_i |
| 10 | 22.200001 | string_j |
+------------+----------------+---------------------------------------------------+
SQLRowCount returns 10
10 rows fetched
SQL> NOTE: One can fiddle with the odbc.ini file and see the effects. For example,
changing the name of the Datassource from [test] to [foo] should produce the following results:
[-bash: ~/OPeNDAP/hyrax/sql_handler] isql -v test
[IM002][unixODBC][Driver Manager]Data source name not found and no default driver specified
[ISQL]ERROR: Could not SQLConnectand
[-bash: ~/OPeNDAP/hyrax/sql_handler] isql -v foo
+---------------------------------------+
| Connected! |
| |
| sql-statement |
| help [tablename] |
| quit |
| |
+---------------------------------------+
SQL> SELECT a,b,c FROM sqlh_table;
+------------+----------------+---------------------------------------------------+
| a | b | c |
+------------+----------------+---------------------------------------------------+
| 1 | 81 | string_a |
| 2 | 61.099998 | string_b |
| 3 | 51 | string_c |
| 4 | 2100 | string_d |
| 5 | 21 | string_e |
| 6 | 4133 | string_f |
| 7 | 31.4 | string_g |
| 8 | 21.299999 | string_h |
| 9 | 11.6 | string_i |
| 10 | 22.200001 | string_j |
+------------+----------------+---------------------------------------------------+
SQLRowCount returns 10
10 rows fetched
SQL> After changing the Datasource source BACK to [test] I saw that at this point the
first three tests began to return valid content so I made baselines:
[-bash: ~/OPeNDAP/hyrax/sql_handler/tests] make chec
[-bash: ~/OPeNDAP/hyrax/sql_handler/tests] ./testsuite 1 2 3
## ----------------------------------------------------------- ##
## sql_module 1.0.0 test suite: bes.conf besstandalone getdap. ##
## ----------------------------------------------------------- ##
1: BESCMD postgresql.das.bescmd ok
2: BESCMD postgresql.dds.bescmd ok
3: BESCMD postgresql.ddx.bescmd ok
## ------------- ##
## Test results. ##
## ------------- ##
All 3 tests were successful.Installed on CentOS-7 latest running in an AWS EC2 instance.
NOTE: I disabled SELinux on this system to simplify my installation/configuration experience.
There are a bewildering number of ways to install MySQL on Linux. However, the most obvious one, yum, is not by default connected to the MySQL repository. The MySQL Linux Installation Guide directs one to connect the MySQL repo to your system. IN a case of misunderstanding I downloaded the Red Hat Enterprise Linux 7 / Oracle Linux 7 (x86, 64-bit), RPM Bundle from the Red Hat Enterprise section of this page: https://dev.mysql.com/downloads/mysql/ The installation required that all of the included rpms be named on the installation command:
tar -xvf mysql-8.0.17-1.el7.x86_64.rpm-bundle.tar;
sudo yum install *.rpm; # Note that the tar ball unpacks all the rpms into the CWDThis successfully install the software.
In order to start the service:
sudo service start mysqldAfter much trashing about I discover that the "root" password for the MySQL service is randomly generated and written to the error log. In order to recover it, perform the following:
% sudo grep 'temporary password' /var/log/mysqld.log
2019-10-07T19:06:18.810367Z 5 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: I_AOA_()d7e0Changing the password goes like this:
% sudo mysql -uroot -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 24
Server version: 8.0.17
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'Your!Spiffy!New!Password123';
Query OK, 0 rows affected (0.00 sec)
mysql> quit
ByeLogin as the mysql root user:
% mysql -uroot -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 26
Server version: 8.0.17 MySQL Community Server - GPL
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.Create and select the the test database:
mysql> CREATE DATABASE test;
Query OK, 1 row affected (0.00 sec)
mysql> USE test;
Database changedCreate the the table sqlh_table:
mysql> CREATE TABLE sqlh_table
-> (a integer NOT NULL DEFAULT 0,
-> b real NOT NULL DEFAULT 0,
-> c varchar(50),
-> CONSTRAINT pk PRIMARY KEY (a));
Query OK, 0 rows affected (0.02 sec)Populate the table sqlh_table with data:
mysql> INSERT INTO sqlh_table(a, b, c) VALUES ('1', '81.0', 'string_a'), ('2', '61.1', 'string_b'), ('3', '51.0', 'string_c'), ('4', '2100.0', 'string_d'), ('5', '21.0', 'string_e'), ('6', '4133.0', 'string_f'), ('7', '31.4', 'string_g'), ('8', '21.3', 'string_h'), ('9', '11.6', 'string_i'), ('10', '22.2', 'string_j');
Query OK, 10 rows affected (0.01 sec)
Records: 10 Duplicates: 0 Warnings: 0Now we need a test user because doing everything as root is probably ill advised: From Creating Accounts
mysql -u root -p
Enter password:
...
mysql> CREATE USER 'mysql'@'localhost' IDENTIFIED BY 'mysqlUserPasswordHere';
Query OK, 0 rows affected (0.01 sec)
mysql> GRANT SELECT ON *.* TO 'mysql'@'localhost' WITH GRANT OPTION;
Query OK, 0 rows affected (0.00 sec)That should be enough to get the tests working, so quit the MySQL client for now:
mysql> quit;I found odbcinst.ini like so:
[root@ip-172-31-9-74 etc]# find / -type f -name odbcinst.ini
/etc/odbcinst.iniAnd this is its content:
[PostgreSQL]
Description=ODBC for PostgreSQL
Driver=/usr/lib/psqlodbcw.so
Setup=/usr/lib/libodbcpsqlS.so
Driver64=/usr/lib64/psqlodbcw.so
Setup64=/usr/lib64/libodbcpsqlS.so
FileUsage=1
[MySQL]
Description=ODBC for MySQL
Driver=/usr/lib/libmyodbc5.so
Setup=/usr/lib/libodbcmyS.so
Driver64=/usr/lib64/libmyodbc5.so
Setup64=/usr/lib64/libodbcmyS.so
FileUsage=1
[MySQL ODBC 8.0 Unicode Driver]
Driver=/usr/lib64/libmyodbc8w.so
SETUP=/usr/lib64/libmyodbc8S.so
UsageCount=1
[MySQL ODBC 8.0 ANSI Driver]
Driver=/usr/lib64/libmyodbc8a.so
SETUP=/usr/lib64/libmyodbc8S.so
UsageCount=1So I added odbc.ini in the same spot with the same permissions:
[root@ip-172-31-9-74 etc]# touch /etc/odbc.ini
[root@ip-172-31-9-74 etc]# chmod 644 !$
chmod 644 /etc/odbc.ini
[root@ip-172-31-9-74 etc]# ls -l /etc/odbc*
-rw-r--r--. 1 root root 0 Oct 8 14:01 /etc/odbc.ini
-rw-r--r--. 1 root root 579 Jun 11 23:20 /etc/odbcinst.iniRetrieve the appropriate files from: MySQL ODBC Connector Downlods
This requires two separete RPM downloads:
- mysql-connector-odbc-8.0.17-1.el7.i686.rpm
- mysql-connector-odbc-setup-8.0.17-1.el7.i686.rpm
This page MySQL ODC Connector Configuration
was of little use, and after a ridculaous amount of additonal thrashing I found that
the odbcinst.ini file had to be modified to accomodate the new drivers:
OLD:
[MySQL]
Description=ODBC for MySQL
Driver=/usr/lib/libmyodbc5.so
Setup=/usr/lib/libodbcmyS.so
Driver64=/usr/lib64/libmyodbc5.so
Setup64=/usr/lib64/libodbcmyS.so
FileUsage=1NEW:
[MySQL]
Description=ODBC for MySQL
Driver=/usr/lib/libmyodbc8w.so
Setup=/usr/lib/libodbcmyS.so
Driver64=/usr/lib64/libmyodbc8w.so
Setup64=/usr/lib64/libodbcmyS.so
FileUsage=1Next I edited the /etc/odbc.ini and added the test database like so:
[test]
Driver = MySQL
Description = LocalMySQL
Trace = Yes
TraceFile = sql.log
Database = test
Servername = localhost
UserName = mysql
Password = *************
Port = 3306After which I was able to connect to the DB using the isql ODBC application:
Invoke isql on the "test" ODBC connection as the user "mysql" with password "passwordy"
% isql test mysql passwordy
+---------------------------------------+
| Connected! |
| |
| sql-statement |
| help [tablename] |
| quit |
| |
+---------------------------------------+
SQL> help sqlh_table
+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+----------+---------------------+------------+--------------+---------------+---------------+---------+-----------------------------------------------------------------+-----------------------------------------------------------------+--------------+-----------------+------------------+-----------------+------------+
| TABLE_CAT | TABLE_SCHEM | TABLE_NAME | COLUMN_NAME | DATA_TYPE| TYPE_NAME | COLUMN_SIZE| BUFFER_LENGTH| DECIMAL_DIGITS| NUM_PREC_RADIX| NULLABLE| REMARKS | COLUMN_DEF | SQL_DATA_TYPE| SQL_DATETIME_SUB| CHAR_OCTET_LENGTH| ORDINAL_POSITION| IS_NULLABLE|
+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+----------+---------------------+------------+--------------+---------------+---------------+---------+-----------------------------------------------------------------+-----------------------------------------------------------------+--------------+-----------------+------------------+-----------------+------------+
| | | sqlh_table | a | 4 | integer | 10 | 4 | 0 | 10 | 0 | | 0 | 4 | | | 1 | NO |
| | | sqlh_table | b | 8 | double | 15 | 8 | | | 0 | | 0 | 8 | | | 2 | NO |
| | | sqlh_table | c | -9 | varchar | 50 | 200 | | | 1 | | | -9 | | 200 | 3 | YES |
+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+----------+---------------------+------------+--------------+---------------+---------------+---------+-----------------------------------------------------------------+-----------------------------------------------------------------+--------------+-----------------+------------------+-----------------+------------+
SQLRowCount returns 3
3 rows fetched
SQL> select * from sqlh_table
+-----------+-------------------------+---------------------------------------------------+
| a | b | c |
+-----------+-------------------------+---------------------------------------------------+
| 1 | 81 | string_a |
| 2 | 61.1 | string_b |
| 3 | 51 | string_c |
| 4 | 2100 | string_d |
| 5 | 21 | string_e |
| 6 | 4133 | string_f |
| 7 | 31.4 | string_g |
| 8 | 21.3 | string_h |
| 9 | 11.6 | string_i |
| 10 | 22.2 | string_j |
+-----------+-------------------------+---------------------------------------------------+
SQLRowCount returns 10
10 rows fetched
SQL> woot.
(That was ridculous, unneccessarily complex, and non-intuitive)
AT this point I had to revist the sql_handler code because there were issues with authentication.
I re-enabled the authentication (see ODBCConnecor.cc lines 70-94) and added the "data" file data/mysql.sql
With the correct user and passoword installed in the aforementioned file the handler was able to connect
to the MySQL service.
At this point the code apears to be failing because of an unanticipated data type.
Running any of these commands in the tests directory:
besstandalone -c bes.conf -d "cerr,all" -i sql/mysql.dds.bescmd
besstandalone -c bes.conf -d "cerr,all" -i sql/mysql.das.bescmd
besstandalone -c bes.conf -d "cerr,all" -i sql/mysql.data.bescmdWill produce this error:
SQLRequestHandler: Error: 7 Message: SQL Handler: The datatype read from the Data Source is not supported. The problem type code is: -8 File: unixODBC/ODBCTypeFactoryComponent.cc Line: 184Hmmmmm
Also of note is that running besstandalone without the debugging enabled does not produce a meaningful messsage:
<Message>SQLRequestHandler: Error occurred, bad constraints specified or no active database server for this dataset. Please,try a less restrictive constraint or contact the administrator.</Message>This should be corrected, in addition to correcting the underlying unanticipated data type bug.