Note: This is a topic for learning how to connect SWI-Prolog to PostgreSQL via ODBC.
If you have Trust level: Basic you can edit this by clicking on the edit icon in the lower right.
Note: Do not reply to this topic; questions, concerns, comments, etc. are to be handled in
Wiki Discussion: SWI-Prolog connecting to PostgreSQL via ODBC
SWI-Prolog connecting to PostgreSQL via ODBC
Ubuntu
Installation Instructions
Language agnostic steps
1. Install PostgreSQL
(If not currently installed)
$ sudo apt update
$ sudo apt install postgresql
2. Install net-tools
(If not currently installed)
$ sudo apt install net-tools
3. Install unixodbc
$ sudo apt install unixodbc
4. Install PostgreSQL ODBC driver
$ sudo apt-get install odbc-postgresql
5. Verify /etc/odbcinst.ini
entries for PostgreSQL
/etc/odbcinst.ini
- Configuration file containing all the database drivers specifications. (ref)
groot@galaxy:~$ cat /etc/odbcinst.ini
[PostgreSQL ANSI]
Description=PostgreSQL ODBC driver (ANSI version)
Driver=psqlodbca.so
Setup=libodbcpsqlS.so
Debug=0
CommLog=1
UsageCount=1
[PostgreSQL Unicode]
Description=PostgreSQL ODBC driver (Unicode version)
Driver=psqlodbcw.so
Setup=libodbcpsqlS.so
Debug=0
CommLog=1
UsageCount=1
6. Create odbc.ini
entry
odbc.ini
needs to be in directory /etc
, e.g. /etc/odbc.ini
(ref 1, ref 2)
ini
files have a specific syntax. (ref)
odbc.ini
files have a specific format (semantics) (ref)
ini
files are text files and can be edited with any text editor such as nano, vim, etc.
/etc/odbc.ini
- is owned by root with only root
having write access, e.g.
$ ls -la /etc/odbc.ini
-rw-r--r-- 1 root root 314 Jun 7 07:49 /etc/odbc.ini
With privileges elevated to root
the file can be edited, e.g. $ sudo nano /etc/odbc.ini
Example of sudo in action.
groot@galaxy:~$ whoami
groot
groot@galaxy:~$ sudo whoami
root
Note: The user groot is not root
.
odbc.ini
contains user-specified access information for the psqlODBC driver.
[SWI-Prolog Discourse]
Description = SWI-Prolog Discourse example
Driver = PostgreSQL Unicode
Servername = localhost
Database = swi_prolog
UserName = groot
Password = <password>
Port = 5432
-
[SWI-Prolog Discourse]
[SWI-Prolog Discourse]
is a user created Data Source Name (DSN). This example has a space and mixed case letters. It is also an ini section name. -
Description = SWI-Prolog Discourse example
SWI-Prolog Discourse example
is just free text used with some of the tools to display a meaningful description. -
Driver = PostgreSQL Unicode
PostgreSQL Unicode
is one of the section names from/etc/odbcinst.ini
. The other section name from the example above isPostgreSQL ANSI
. -
Servername = localhost
The servername value uses the format[protocol:]server[,port]
, e.g.localhost
.
The servername property is optional but included here to ensure it is set correctly.
localhost
is theserver
(hostname) that refers to the current computer. (ref) -
Database = swi_prolog
swi_prolog
is a database name in the PostgreSQL database
The database name is the name used with the createdb command,
e.g.createdb swi_prolog
or can be displayed usingpsql
with the \l command.
groot@galaxy:~$ psql postgres
psql (10.12 (Ubuntu 10.12-0ubuntu0.18.04.1))
Type "help" for help.
postgres=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
--------------------------+----------+----------+---------+---------+-----------------------
postgres | postgres | UTF8 | C.UTF-8 | C.UTF-8 |
swi_prolog | groot | UTF8 | C.UTF-8 | C.UTF-8 |
- UserName = groot
groot
is a user of the corresponding databaseswi_prolog
, e.g.
postgres=# \c swi_prolog
You are now connected to database "swi_prolog" as user "groot".
swi_prolog=# SELECT usename FROM pg_user;
usename
----------
postgres
groot
(2 rows)
-
Password = <password>
<password>
is the password used by correspondening user (groot
). Note: In creating this Wiki a password was created for usergroot
as the normal access via psql did not use a password. -
Port = 5432
5432
is the default port for PostgreSQL. (ref)
For demonstration purposes here, the swi_prolog
schema is from Discourse and the data is the posts from the SWI-Prolog Discourse forum.
7. Start the PostgreSQL server
$ sudo service postgresql start
8. Check that PostgreSQL is listening on port 5432
groot@galaxy:~$ netstat -nlp | grep postgresql
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN -
unix 2 [ ACC ] STREAM LISTENING 15782 - /var/run/postgresql/.s.PGSQL.5432
9. Test ODBC settings using iusql
$ iusql -v "SWI-Prolog Discourse"
+---------------------------------------+
| Connected! |
| |
| sql-statement |
| help [tablename] |
| quit |
| |
+---------------------------------------+
SQL> SELECT COUNT(*) FROM posts;
+---------------------+
| count |
+---------------------+
| 10082 |
+---------------------+
SQLRowCount returns 1
1 rows fetched
SQL> quit
Language specific steps (SWI-Prolog)
10. Install SWI-Prolog ODBC
$ sudo apt-get install swi-prolog-odbc
Note: For this wiki SWI-Prolog was installed using the PPA. If you build from the sources, the SWI-Prolog interface may already be installed. (I plan to check if the PPA does not install the SWI-Prolog ODBC libraries).
11. Create Prolog example
<Absolute path>/<File name>
e.g.
~/projects/prolog/odbc_wiki/odbc_example.pl
<Absolute path> - ~/projects/prolog/odbc_wiki
<File name> - odbc_example.pl
Note: File name must have file type pl
so that consult/1 can be used.
:- use_module(library(odbc)).
example :-
odbc_connect('SWI-Prolog Discourse', Connection, []),
odbc_query(Connection, 'SELECT COUNT(*) FROM posts',COUNT),
format('posts rows: ~w~n',[COUNT]),
odbc_disconnect(Connection).
12. Start SWI-Prolog
a. Using Ubuntu change directory to <Absolute path>, e.g.
groot@galaxy:~$ cd ~/projects/prolog/odbc_wiki
groot@galaxy:~/projects/prolog/odbc_wiki$
b. Start SWI-Prolog, e.g.
groot@galaxy:~/projects/prolog/odbc_wiki$ swipl
Welcome to SWI-Prolog (threaded, 64 bits, version 8.1.32)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.
For online help and background, visit https://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).
?-
c. Check access to <File name>, e.g.
?- working_directory(D,D),directory_files(D,Files).
D = '/home/groot/projects/prolog/odbc_wiki/',
Files = ['odbc_example.pl', .., '.'].
13. Run example
a. consult(<File name>), e.g.
?- consult('odbc_example.pl').
true.
b. Run example, e.g.
?- example.
posts rows: row(10082)
true.
Simple test cases
:- use_module(library(odbc)).
:- begin_tests('swi-prolog ODBC').
test('odbc data source') :-
odbc_data_source('SWI-Prolog Discourse', Driver),
assertion( Driver = 'PostgreSQL Unicode' ).
test(connections,
[ setup(odbc_connect('SWI-Prolog Discourse', Connection, [])),
cleanup(odbc_disconnect(Connection))
]) :-
odbc_current_connection(Connection, DSN),
assertion( DSN == 'SWI-Prolog Discourse' ).
test('connection property',
[ setup(odbc_connect('SWI-Prolog Discourse', Connection, [])),
cleanup(odbc_disconnect(Connection))
]) :-
odbc_get_connection(Connection, database_name(Database_name)),
odbc_get_connection(Connection, dbms_name(DBMS_name)),
odbc_get_connection(Connection, dbms_version(DBMS_version)),
odbc_get_connection(Connection, driver_name(Driver_name)),
odbc_get_connection(Connection, driver_odbc_version(Driver_odbc_version)),
odbc_get_connection(Connection, driver_version(Driver_version)),
odbc_get_connection(Connection, active_statements(Active_statements)),
assertion( Database_name == swi_prolog ),
assertion( DBMS_name == 'PostgreSQL' ),
assertion( DBMS_version == '10.0.12' ),
assertion( Driver_name == 'psqlodbcw.so' ),
assertion( Driver_odbc_version == '03.51' ),
assertion( Driver_version == '12.01.0000' ),
assertion( Active_statements == 0 ).
test('odbc_query - count',
[ setup(odbc_connect('SWI-Prolog Discourse', Connection, [])),
cleanup(odbc_disconnect(Connection))
]) :-
odbc_query(Connection, 'SELECT COUNT(*) FROM posts', Row),
assertion( Row == row(10082) ).
test('odbc_query - single row, single column',
[ setup(odbc_connect('SWI-Prolog Discourse', Connection, [])),
cleanup(odbc_disconnect(Connection))
]) :-
odbc_query(Connection, 'SELECT raw FROM posts WHERE id = 1', Row),
assertion( Row == row('A category exclusive to members with trust level 3 and higher.') ).
test('odbc_query - multiple rows, single column',
[ setup(odbc_connect('SWI-Prolog Discourse', Connection, [])),
cleanup(odbc_disconnect(Connection)),
all(Rows ==
[
row('A category exclusive to members with trust level 3 and higher.'),
row('Discussion about this site, its organization, how it works, and how we can improve it.')
]
)
]) :-
odbc_query(Connection, 'SELECT raw FROM posts WHERE id in (1,2)', Rows).
test('odbc_query - multiple rows, multiple columns',
[ setup(odbc_connect('SWI-Prolog Discourse', Connection, [])),
cleanup(odbc_disconnect(Connection)),
all(Rows ==
[
row(1,'A category exclusive to members with trust level 3 and higher.'),
row(2,'Discussion about this site, its organization, how it works, and how we can improve it.')
]
)
]) :-
odbc_query(Connection, 'SELECT id,raw FROM posts WHERE id in (1,2)', Rows).
fetch_result(row(1,'A category exclusive to members with trust level 3 and higher.')).
fetch_result(row(2,'Discussion about this site, its organization, how it works, and how we can improve it.')).
fetch_result(row(3,'Private category for staff discussions. Topics are only visible to admins and moderators.')).
fetch_result(row(5,'Edit the first post in this topic to change the contents of the Terms of Service page.')).
test('odbc_fetch',
[ setup(odbc_connect('SWI-Prolog Discourse', Connection, [])),
cleanup(odbc_disconnect(Connection))
]) :-
odbc_set_connection(Connection, cursor_type(static)),
odbc_prepare(Connection,
'SELECT id,raw FROM posts WHERE id BETWEEN 1 AND 5 AND LENGTH(raw) < 100 ORDER BY id',
[],
Statement,
[ fetch(fetch) ]),
odbc_execute(Statement, []),
fetch(Statement, next).
fetch(Statement, Options) :-
odbc_fetch(Statement, Row, Options),
( Row == end_of_file
-> true
; assertion(fetch_result(Row)),
fetch(Statement, Options)
).
:- end_tests('swi-prolog ODBC').
System test cases
See: GitHub SWI-Prolog - packages-odbc / test_odbc.pl
Using odbcinst
To print config info
groot@galaxy:~$ odbcinst -j
unixODBC 2.3.4
DRIVERS............: /etc/odbcinst.ini
SYSTEM DATA SOURCES: /etc/odbc.ini
FILE DATA SOURCES..: /etc/ODBCDataSources
USER DATA SOURCES..: /home/groot/.odbc.ini
SQLULEN Size.......: 8
SQLLEN Size........: 8
SQLSETPOSIROW Size.: 8
To list Drivers
groot@galaxy:~$ odbcinst -q -d
[PostgreSQL ANSI]
[PostgreSQL Unicode]
To list DSNs
groot@galaxy:~$ odbcinst -q -s
[SWI-Prolog Discourse]
Windows
Note: If there is enough demand a Windows version of this can be created.