Nicos sqlite interface

This commit is contained in:
Vítor Santos Costa 2014-07-15 18:56:45 -05:00
parent 4d7b270ce4
commit 3268920dc8
9 changed files with 1994 additions and 0 deletions

View File

@ -0,0 +1,16 @@
SOBJ= $(PACKSODIR)/prosqlite.$(SOEXT)
CFLAGS+=-std=c99
LIBS= -lsqlite3
all: $(SOBJ)
$(SOBJ): c/prosqlite.o
mkdir -p $(PACKSODIR)
$(LD) $(LDSOFLAGS) -o $@ $(SWISOLIB) $< $(LIBS)
check::
install::
clean:
rm -f c/prosqlite.o
distclean: clean
rm -f $(SOBJ)

View File

@ -0,0 +1,85 @@
################################################################
# Makefile template for SWI-Prolog PROSQLITE interface
#
# This template is used by configure to create Makefile. See
# the file INSTALL for further installation instructions.
#
# License: LGPL
#
# Author: Nicos Angelopoulos & Jan Wielemaker (jan@swi.psy.uva.nl)
################################################################
PACKAGE=prosqlite
DOC=prosqlite
include ../Makefile.defs
CFLAGS+= @PROSQLITE_CFLAGS@ -I.
LDSOFLAGS+= @PROSQLITE_LDFLAGS@
LIBS=@LIBS@
NETLIBS=@NETLIBS@
LIBPL= @PROSQLITE_PLTARGETS@
TARGETS= @PROSQLITE_TARGETS@
PROSQLITEOBJ= prosqlite.o
all: $(TARGETS)
nolib::
@echo "WARNING: Could not find sqlite library; skipped"
prosqlite.o: $(srcdir)/c/prosqlite.c
$(CC) -c $(CFLAGS) $< -o $@
prosqlite.@SO@: $(PROSQLITEOBJ)
$(LD) $(LDSOFLAGS) -o $@ $(AROBJ) @PROSQLITE_LIBS@ $(LIBPLSO)
install: $(TARGETS) $(addprefix $(srcdir)/, $(LIBPL)) install-examples
mkdir -p $(DESTDIR)$(SOLIBDIR)
for f in $(TARGETS); do \
[ "$$f" = nolib ] || $(INSTALL_PROGRAM) $$f $(DESTDIR)$(SOLIBDIR); \
done
mkdir -p $(DESTDIR)$(PLLIBDIR)
for f in $(LIBPL); do \
$(INSTALL_DATA) $(srcdir)/$$f $(DESTDIR)$(PLLIBDIR); \
done
$(MKINDEX)
ln-install::
@$(MAKE) INSTALL_DATA='../ln-install' INSTALL_PROGRAM='../ln-install' install
rpm-install: install
html-install::
mkdir -p $(DESTDIR)$(PKGDOCDIR)
$(INSTALL) -m 644 $(DOC).html $(DESTDIR)$(PKGDOCDIR)
pdf-install::
mkdir -p $(DESTDIR)$(PKGDOCDIR)
$(INSTALL) -m 644 $(DOC).pdf $(DESTDIR)$(PKGDOCDIR)
nnuninstall::
(cd $(SOLIBDIR) && rm -f $(TARGETS))
(cd $(PLLIBDIR) && rm -f $(LIBPL))
$(PL) -f none -g make -t halt
################################################################
# Check
################################################################
check::
################################################################
# Clean
################################################################
clean:
rm -f $(AROBJ) *~ *.o *% a.out core config.log
distclean: clean
rm -f $(TARGETS) config.cache config.h config.status Makefile
rm -f $(DOC).aux $(DOC).log $(DOC).out $(DOC).toc
rm -rf autom4te.cache

View File

@ -0,0 +1,39 @@
proSQLite: a Prolog interface to the SQLite database system.
--------------------
This Prolog library was developed on SWI-Prolog 6.1.12 under linux.
It should compile easily on linux or MACs. It is also likely
that it compiles easily for YAP Prolog.
On SWI-Prolog is higly recommended that you install from with the engine
via
?- pack_install( prosqlite ).
This takes care of everything and you can then load the library via
?- [library(prosqlite)].
If you need to compile from sources, edit buildenv.sh to fit your system
and in a bourne-compatible shell do :
$ source env/buildenv.sh
$ make
The publication corresponding to this library is :
Exploring file based databases via an Sqlite interface
Canisius Sander, Nicos Angelopoulos and Lodewyk Wessels
ICLP Workshop on Logic-based methods in Programming Environments (September, 2012. Budapest, Hungary).
---------
Nicos Angelopoulos, November 2013. (0.1.0 @ August 2012)
---
http://stoics.org.uk/~nicos/
nicos_mlists_06@yahoo.co.uk

View File

@ -0,0 +1,345 @@
#include <sqlite3.h>
#include <SWI-Stream.h>
#include <SWI-Prolog.h>
#include <stdio.h>
#include <stdbool.h>
atom_t row_atom;
functor_t minus2_functor;
static atom_t ATOM_true;
static atom_t ATOM_false;
int PL_SQLite_Connection_release(atom_t connection)
{
printf("release\n");
return 1;
}
PL_blob_t PL_SQLite_Connection = {
PL_BLOB_MAGIC,
PL_BLOB_UNIQUE | PL_BLOB_NOCOPY,
"SQLiteConnection",
PL_SQLite_Connection_release, // release
0, // compare
0, // write
0 // acquire
};
static foreign_t c_sqlite_connect(term_t filename, term_t connection)
{
char *filename_c;
if (PL_get_atom_chars(filename, &filename_c))
{
sqlite3* handle;
if (sqlite3_open(filename_c, &handle) == SQLITE_OK)
{
return PL_unify_blob(connection, handle, sizeof(sqlite3*),
&PL_SQLite_Connection);
}
}
PL_free(filename_c);
PL_fail;
}
static foreign_t c_sqlite_disconnect(term_t connection)
{
sqlite3* db;
if (PL_get_blob(connection, (void**)&db, 0, 0))
{
if (sqlite3_close(db) == SQLITE_OK)
{
PL_succeed;
} else {
printf("SQLite returned error at closing database \n");
PL_fail;
};
}
printf("could not get connection to close \n");
PL_fail;
}
typedef struct query_context
{
sqlite3_stmt* statement;
functor_t row_functor;
int num_columns;
int* column_types;
} query_context;
query_context* new_query_context(sqlite3_stmt* statement)
{
int i;
query_context* context = (query_context*)malloc(sizeof(query_context));
context->num_columns = sqlite3_column_count(statement);
context->row_functor = PL_new_functor(row_atom, context->num_columns);
context->statement = statement;
context->column_types = (int*)malloc(sizeof(int) * context->num_columns);
for (i = 0; i < context-> num_columns; i++)
context->column_types[i] = sqlite3_column_type(statement, i);
return context;
}
void free_query_context(query_context* context)
{
sqlite3_finalize(context->statement);
free(context->column_types);
free(context);
}
int unify_row_term(term_t row, query_context* context)
{
int i;
if (!PL_unify_functor(row, context->row_functor))
PL_fail;
for (i = 0; i < context->num_columns; i++)
{
term_t col_term = PL_new_term_ref();
switch (context->column_types[i])
{
case SQLITE_INTEGER:
if ( !PL_put_integer(col_term, sqlite3_column_int(context->statement, i)) )
return FALSE;
break;
case SQLITE_FLOAT:
if ( !PL_put_float(col_term, sqlite3_column_double(context->statement, i)) )
return FALSE;
break;
case SQLITE_TEXT:
if (sqlite3_column_bytes(context->statement,i))
PL_put_atom_chars(col_term, sqlite3_column_text(context->statement, i));
else
// this should probably never be the case (should be SQLITE_NULL) but firefox's places.sqlite
// has 0 length texts
PL_put_atom_chars(col_term, "" );
break;
case SQLITE_BLOB:
// TODO: what prolog type should BLOBs be mapped to?
// PL_put_blob?
break;
case SQLITE_NULL:
// TODO: what about this?
// PL_put_nil?
if (sqlite3_column_bytes(context->statement,i))
// this should probably never be the case (should be SQLITE_TEXT?) but firefox's places.sqlite
// has non 0 length nulls
PL_put_atom_chars(col_term, sqlite3_column_text(context->statement, i));
else
// PL_put_nil(col_term) // would be more correct probably
PL_put_atom_chars(col_term, "" );
break;
}
if (!PL_unify_arg(i + 1, row, col_term))
PL_fail;
}
PL_succeed;
}
static foreign_t c_sqlite_version(term_t ver, term_t datem)
{
term_t tmp = PL_new_term_ref();
if ( PL_unify_term(tmp,PL_FUNCTOR_CHARS,":",2,PL_INT, 1, PL_INT, 2) && // Minor + Fix
PL_unify_term(ver,PL_FUNCTOR_CHARS,":",2,PL_INT, 0, PL_TERM, tmp ) && // Major
PL_unify_term(datem,PL_FUNCTOR_CHARS,"date",3,PL_INT, 2013, PL_INT, 11, PL_INT, 1) )
return TRUE;
else
return FALSE;
return FALSE;
// PL_unify_term(ver,PL_FUNCTOR_CHARS,":",1,PL_CHARS,
}
int raise_sqlite_exception(sqlite3* db)
{
term_t except = PL_new_term_ref();
if ( PL_unify_term(except,
PL_FUNCTOR_CHARS, "sqlite_error", 2,
PL_INT, sqlite3_errcode(db),
PL_CHARS, sqlite3_errmsg(db)) )
return PL_raise_exception(except);
return FALSE;
}
// Copied and adapted from the odbc package
int formatted_string(term_t in, char** out)
{
term_t av = PL_new_term_refs(3);
static predicate_t format;
//char *out = NULL;
size_t len = 0;
*out = NULL;
IOSTREAM *fd = Sopenmem(out, &len, "w");
if (!fd)
return FALSE; /* resource error */
if (!format)
format = PL_predicate("format", 3, "user");
if (!PL_unify_stream(av+0, fd) ||
!PL_get_arg(1, in, av+1) ||
!PL_get_arg(2, in, av+2) ||
!PL_call_predicate(NULL, PL_Q_PASS_EXCEPTION, format, av))
{
Sclose(fd);
if (*out)
PL_free(*out);
return FALSE;
}
Sclose(fd);
if (*out)
PL_free(*out);
return TRUE;
}
int get_query_string(term_t tquery, char** query)
{
if (PL_is_functor(tquery, minus2_functor))
return formatted_string(tquery, query);
else
return PL_get_chars(tquery, query, CVT_ATOM|CVT_STRING|BUF_MALLOC|REP_UTF8);
}
// Jan says "You must call PL_free() on the query strings after you are done with them!"
// fixme: check here or at Prolog that Connection/Alias IS open: otherwise
// we get sqlite_error(21,library routine called out of sequence)
static foreign_t c_sqlite_query(term_t connection, term_t query, term_t row,
control_t handle)
{
sqlite3* db;
query_context* context;
term_t tmp = PL_new_term_ref();
int changes = 0;
switch (PL_foreign_control(handle))
{
case PL_FIRST_CALL:
PL_get_blob(connection, (void**)&db, 0, 0);
char* query_c;
sqlite3_stmt* statement;
if (!get_query_string(query, &query_c))
{
PL_free(query_c);
PL_fail;
};
if (sqlite3_prepare(db, query_c, -1, &statement, 0) != SQLITE_OK)
{ PL_free(query_c);
return raise_sqlite_exception(db);
}
PL_free(query_c);
bool recurrent = false;
switch (sqlite3_step(statement))
{
case SQLITE_ROW:
context = new_query_context(statement);
recurrent = true;
if ( unify_row_term(row, context) )
PL_retry_address(context);
/*FALLTHROUGH*/
case SQLITE_DONE:
if (recurrent)
{
free_query_context(context);
PL_fail;
} else
{
int what = sqlite3_column_count(statement);
if (what) { /* >0 means statement is supposed to return results */
sqlite3_finalize(statement);
PL_fail;
}
else { /* statement is a INSERT/DELETE/UPDATE which do not return anything */
context = new_query_context(statement);
changes = sqlite3_changes(db);
if (PL_unify_term(tmp,PL_FUNCTOR_CHARS,"row",1,PL_INT64, (int)changes))
if( !PL_unify(row,tmp) )
{ free_query_context(context);
PL_fail;}
free_query_context(context);
PL_succeed;
};
}
}
case PL_REDO:
context = PL_foreign_context_address(handle);
switch (sqlite3_step(context->statement))
{
case SQLITE_ROW:
if ( unify_row_term(row, context) )
PL_retry_address(context);
/*FALLTHROUGH*/
case SQLITE_DONE:
free_query_context(context);
PL_fail;
}
case PL_PRUNED:
context = PL_foreign_context_address(handle);
free_query_context(context);
PL_succeed;
}
PL_fail;
}
install_t install_prosqlite()
{
ATOM_true = PL_new_atom("true");
ATOM_false = PL_new_atom("false");
row_atom = PL_new_atom("row");
minus2_functor = PL_new_functor(PL_new_atom("-"), 2);
PL_register_foreign("c_sqlite_version", 2, c_sqlite_version, 0);
PL_register_foreign("c_sqlite_connect", 2, c_sqlite_connect, 0);
PL_register_foreign("c_sqlite_disconnect", 1, c_sqlite_disconnect, 0);
PL_register_foreign("c_sqlite_query", 3, c_sqlite_query,
PL_FA_NONDETERMINISTIC);
}
install_t uninstall_prosqlite()
{
PL_unregister_atom(row_atom);
PL_unregister_blob_type(&PL_SQLite_Connection);
}

View File

@ -0,0 +1,81 @@
dnl Process this file with autoconf to produce a configure script.
m4_ifdef([HAS_TOP],[m4_ignore],[
AC_INIT(install-sh)
AC_PREREQ([2.50])
AC_CONFIG_HEADER(config.h)
])
PKG_PROSQLITE="packages/prosqlite"
AC_SUBST(PKG_PROSQLITE)
AC_SUBST(PROSQLITE_TARGETS)
AC_SUBST(PROSQLITE_PLTARGETS)
AC_SUBST(PROSQLITE_CFLAGS)
AC_SUBST(PROSQLITE_LDFLAGS)
AC_SUBST(PROSQLITE_LIBS)
m4_ifdef([HAS_TOP],[m4_ignore],[
m4_include([../ac_swi_c.m4])
])
yap_cv_prosqlite=yes
yap_cv_prosqlite=yes
AC_ARG_WITH(sqlite,
[ --with-prosqlite(=location) interface to sqlite],
if test "$withval" = yes; then
PROSQLITE_DIR=/usr
elif test "$withval" = no; then
yap_cv_prosqlite=no
else
PROSQLITE_DIR=$withval
fi,
[PROSQLITE_DIR=/usr ])
havelib=no
if test $yap_cv_prosqlite = yes; then
OCFLAGS="${CFLAGS}"
OCPPFLAGS="${CPPFLAGS}"
OCIFLAGS="${CIFLAGS}"
OLDFLAGS="${LDFLAGS}"
if test ! -z "$PROSQLITE_DIR"; then
PROSQLITE_CFLAGS="-I${PROSQLITE_DIR}/include"
PROSQLITE_LDFLAGS="-L${PROSQLITE_DIR}/lib"
CFLAGS="-I${PROSQLITE_DIR}/include ${CFLAGS}"
CPPFLAGS="-I${PROSQLITE_DIR}/include ${CPPFLAGS}"
CIFLAGS="-I${PROSQLITE_DIR}/include ${CIFLAGS}"
LDFLAGS="-L${PROSQLITE_DIR}/lib ${LDFLAGS}"
fi
AC_CHECK_LIB(sqlite3, sqlite3_open,
PROSQLITE_LIBS="-lsqlite3 $LIBS"
havelib=yes)
AC_CHECK_HEADERS(sqlite3.h, [], [])
CFLAGS="${OCFLAGS}"
CPPFLAGS="${OCPPFLAGS}"
CIFLAGS="${OCIFLAGS}"
LDFLAGS="${OLDFLAGS}"
fi
if test "$havelib" = yes &&
test "$ac_cv_header_sqlite3_h" = yes; then
PROSQLITE_TARGETS="prosqlite.$SO"
PROSQLITE_PLTARGETS="prolog/prosqlite.pl"
else
echo "ERROR: Cannot find odbc library or the header sql.h"
echo "WARNING: PROSQLITE interface will not be built"
PROSQLITE_TARGETS=""
fi
m4_ifdef([HAS_TOP],[m4_ignore],[
AC_OUTPUT(Makefile)
])

View File

@ -0,0 +1,343 @@
/* $Id$
CSS for PlDoc, the SWI-Prolog source documentation system
Author: Jan Wielemaker
Copying: Public domain
For a tutorial on CSS, see
<http://www.htmldog.com/guides/cssbeginner/selectors/>
*/
h1.file, h1.module, h1.dir, h1.wiki
{ background: url("h1-bg.png") top left;
}
h2.wiki
{ background: url("h2-bg.png") top left;
}
.undef
{ color: red;
}
a.builtin
{ color: navy;
}
.defbody
{ margin-bottom: 1em;
}
.arglist
{ font-style: italic;
}
table.paramlist td
{ vertical-align: top;
}
.pred, .det, .functor
{ font-weight: bold;
}
span.pred-tag
{ float: right;
font-size: 80%;
font-style: italic;
color: #202020;
}
dl.tags dt
{ font-weight: bold;
}
dt.keyword-bug, dt.keyword-deprecated, dt.keyword-tbd
{ font-weight: bold;
color: red;
}
dt.pubdef
{ background: url("pub-bg.png") top left;
}
dt.multidef
{ background: url("multi-bg.png") top left;
}
dt.privdef
{ background: url("priv-bg.png") top left;
}
dt.undoc, h2.undoc
{ background-color: #ffbc87;
}
img.action
{ border: 0;
padding-top: 0.2em;
height: 1em;
}
dl.tags
{ font-size: 80%;
margin-top: 0.5em;
}
dl.latex, ul.latex, ol.latex
{ margin-top: 0.7em;
margin-bottom: 1em;
}
dl.termlist
{ margin-top: 0.7em;
margin-bottom: 1em;
margin-left: 1.5em;
}
dl.undoc
{ margin-top: 0.7em;
}
dl.wiki
{ margin-top: 0.7em;
margin-bottom: 1em;
margin-left: 1.5em;
}
dl.wiki dt
{ font-weight: bold;
font-size: 90%;
}
.argdescr
{ padding-left: 1em;
}
table.wiki
{ margin-top: 0.7em;
margin-left: 1.5em;
border: 1px outset blue;
border-spacing: 0;
}
table.wiki td
{ border: 1px inset gray;
padding: 3px;
vertical-align: top;
}
img.icon
{ border: 0;
height: 32px;
float: right;
padding-top: 4px;
}
pre.code
{ margin-left: 1.5em;
margin-right: 1.5em;
border: 1px dotted;
padding-top: 5px;
padding-left: 5px;
padding-bottom: 5px;
background-color: #f8f8f8;
}
/* Navigation bar */
div.navhdr
{ border: 1px dotted;
padding: 5px 5px 3px 5px; /* top,right,bottom,left */
background-color: #f0f0f0;
}
div.navhdr div.jump
{ float: left;
}
div.navhdr div.search
{ float: right;
}
div.src_formats
{ text-align: right;
color: #444;
font: smaller;
border: 1px #ccc solid;
padding-right: 5px;
margin-bottom: 2px;
}
/* Summary index */
table.summary
{ width: 100%;
margin-left: 0;
margin-right: 0;
}
table.summary th.category
{ text-align: center;
font-size: 150%;
font-weight: bold;
border: 1px dotted;
padding-top: 0.5em;
padding-bottom: 0.4em;
background-color: #f0f0f0;
}
table.summary th.section
{ text-align: left;
background-color: #bbffba;
}
table.summary th
{ text-align: left;
background-color: #b9daff;
}
table.summary td
{ /*overflow:hidden; white-space:nowrap; text-overflow:ellipsis;*/
vertical-align: top;
}
tr.private
{ color: #707070;
background-color: #fbffa6;
}
/* Search */
span.for
{ font-style: italic;
}
div.search-results
{ font-size: larger;
font-weight: bold;
margin-top: 1em;
margin-bottom: 0.5em;
}
div.search-counts
{ font-style: italic;
font-size: smaller;
margin-bottom: 1em;
}
div.search-options
{ font-size: 70%;
}
span.search-in, span.search-match
{ border: 1px inset;
padding: 3px 5px 1px 1px;
}
span.search-help
{ padding-left: 1em;
}
div.synopsis
{ text-align: right;
margin-top: 15pt;
}
span.synopsis-hdr
{ font-weight: bold;
padding-right: 1ex;
}
div.synopsis code
{ background-color: #e0e0e0;
}
span.autoload, span.builtin, span.cfunc
{ margin-left: 5pt;
font-style: italic;
}
span.anot
{ float: right;
margin-right: 5pt;
font-style: italic;
font-size: smaller;
padding-top: 0.2em;
color: #444;
}
span.file_anot
{ vertical-align: text-top;
margin-right: 5pt;
font-style: italic;
font-size: 60%;
color: #444;
}
/* Footnotes from latex2html */
sup.fn { color: blue; text-decoration: underline; }
span.fn-text { display: none; }
sup.fn span {display: none;}
sup:hover span
{ display: block !important;
position: absolute; top: auto; left: auto; width: 80%;
color: #000; background: white;
border: 2px solid;
padding: 5px; margin: 10px; z-index: 100;
font-size: 80%;
}
span.sec-nr
{
}
span.sec-title
{ margin-left: 0.5em;
}
/* Listing support */
dl.comment, div.comment
{ color: #006400;
border: 2px inset;
padding: 5px 5px 0px 5px;
background-color: #e5e5e5;
margin-top: 2em;
margin-bottom: 1em;
}
pre.listing
{ margin-top: 0;
margin-bottom: 0;
}
span.line-no
{ font-weight: normal;
font-size: 0.8em;
color: #505050;
background-color: white;
border: 1px inset;
margin-right: 1em;
}
div.comment h1.module
{ margin-top: 0;
}
a.prolog_version
{ font-style: italic;
font-size: 70%;
}
a.nofile /* non-existing file-link */
{ color: black;
font-family: monospace;
text-decoration: none;
}
a.nofile:hover /* non-existing file-link */
{ color: red;
text-decoration: underline;
}

View File

@ -0,0 +1,311 @@
<html>
<head>
<title>prosqlite.pl</title>
<link href="pldoc.css" rel="stylesheet" type="text/css">
<meta
http-equiv="content-type"
content="text/html; charset=UTF-8">
</head>
<body>
<div class="navhdr">
<div class="jump">
<div>
<a class="prolog_version" href="http://www.swi-prolog.org"> SWI-Prolog 6.3.19</a>
</div>
</div>
<div class="search">
</div>
<br clear="right">
</div>
<h1 class="file">prosqlite.pl -- proSQLite: a Prolog interface to the SQLite database system.</h1>
<p>This library follows the design and borrows code from the ODBC library of SWI-Prolog
<a
href="http://www.swi-prolog.org/pldoc/packasqlite_connectge/odbc.html">http://www.swi-prolog.org/pldoc/packasqlite_connectge/odbc.html</a> .</p>
<p>The SQLite system is a powerful zero-configuration management systme that interacts
with single-file databases that are cross-platform compatible binaries.</p>
<p>ProSQLite provides three layers of interaction with SQLite databases.
At the lower level is the querying via SQL statements. A second layer
allows the interogation of the database dictionary, and the final level
facilitates the viewing of database tables as predicates.
See the publication pointed to by <a href="#sqlite_citation/2">sqlite_citation/2</a>, for further details.
If you use prosqlite in your research, please consider citing this publication.</p>
<p>The library has been developed and tested on SWI 6.3.2 but it should
also work on YAP Prolog.</p>
<p>The easiest way to install on SWI is via the package manager.
Simply do:</p>
<pre class="code" ext=""> ?- pack_install( prosqlite ).</pre>
<p>And you are good to go.</p>
<p>There are good/full examples in the sources, directory examples/.
For instance test by :</p>
<pre class="code" ext=""> ?- [predicated].
?- predicated.</pre>
<p>There is a sister package, db_facts (also installable via the manager).
Db_facts, allow interaction with the underlying database via Prolog terms,
That library can also be used as a common compatibility layer for the ODBC
and proSQLite libraries of SWI-Prolog, as it works on both type of connections.</p>
<p>ProSQLite is <a href="/doc_for?object=prolog_debug:debug/1">debug/1</a> aware: call <code>debug(sqlite)</code> to see what is sent to
the sqlite engine.</p>
<p>There are MS wins DLLs included in the sources and recent version of the SWI package
manager will install these properly.</p>
<dl class="tags">
<dt class="keyword-author">author</dt>
<dd class="keyword-author">- Nicos Angelopoulos</dd>
<dd class="keyword-author">- Sander Canisius</dd>
<dt class="keyword-version">version</dt>
<dd class="keyword-version">- 0.1.2, 2013/11/1</dd>
<dt class="keyword-see">See also</dt>
<dd class="keyword-see">- Sander Canisius, Nicos Angelopoulos and Lodewyk Wessels. proSQLite: Prolog file based databases via an SQLite interface. In the proceedings of Practical Aspects of Declarative languages (PADL 2013), (2013, Rome, Italy).</dd>
<dd class="keyword-see">- Sander Canisius, Nicos Angelopoulos and Lodewyk Wessels. Exploring file based databases via an Sqlite interface. In the ICLP Workshop on Logic-based methods in Programming Environments, p. 2-9, (2012, Budapest, Hungary).</dd>
<dd class="keyword-see">- <a href="http://stoics.org.uk/~nicos/pbs/wlpe2012_sqlite.pdf">http://stoics.org.uk/~nicos/pbs/wlpe2012_sqlite.pdf</a></dd>
<dd class="keyword-see">- <a href="http://stoics.org.uk/~nicos/sware/prosqlite">http://stoics.org.uk/~nicos/sware/prosqlite</a></dd>
<dd class="keyword-see">- <a href="http://stoics.org.uk/~nicos/sware/db_facts">http://stoics.org.uk/~nicos/sware/db_facts</a></dd>
<dd class="keyword-see">- <a href="http://www.sqlite.org/">http://www.sqlite.org/</a></dd>
<dd class="keyword-see">- files in examples/ directory</dd>
<dd class="keyword-see">- also available as a SWI pack <a href="http://www.swi-prolog.org/pack/list">http://www.swi-prolog.org/pack/list</a></dd>
<dt class="keyword-license">license</dt>
<dd class="keyword-license">- Perl Artistic License</dd>
<dt class="keyword-tbd">To be done</dt>
<dd class="keyword-tbd">- set pragmas</dd>
</dl>
<dl>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_version/2">
<b class="pred">sqlite_version</b>
<var class="arglist">(-Version, -Date)</var>
</a>
</dt>
<dd class="defbody">The current version. <var>Version</var> is a Mj:Mn:Fx term, and date is a <code>date(Y,M,D)</code> term.</dd>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_binary_version/2">
<b class="pred">sqlite_binary_version</b>
<var class="arglist">(-Version, -Date)</var>
</a>
</dt>
<dd class="defbody">The current version of the binaries. If the installed binaries are not compiled from
the sources, then this might be different (=older) that the sqlite Porlog source version
returned by <a href="#sqlite_version/2">sqlite_version/2</a>. <var>Version</var> is a Mj:Mn:Fx term, and date is a <code>date(Y,M,D)</code> term.</dd>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_citation/2">
<b class="pred">sqlite_citation</b>
<var class="arglist">(-Atom, -Bibterm)</var>
</a>
</dt>
<dd class="defbody">Succeeds once for each publication related to this library. <var>Atom</var> is the atom representation
suitable for printing while <var>Bibterm</var> is a <code>bibtex(Type,Key,Pairs)</code> term of the same publication.
Produces all related publications on backtracking.</dd>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_connect/2">
<b class="pred">sqlite_connect</b>
<var class="arglist">(+File, ?Alias)</var>
</a>
</dt>
<dd class="defbody">Open a connection to an sqlite <var>File</var>. If <var>Alias</var> is a variable, an opaque atom
is generated and unified to it. The opened db connection to file can be accessed via <var>Alias</var>.
<pre class="code" ext=""> sqlite_connect('uniprot.sqlite', uniprot).</pre>
</dd>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_connect/3">
<b class="pred">sqlite_connect</b>
<var class="arglist">(+File, ?Connection, +Options)</var>
</a>
</dt>
<dd class="defbody">Open a connection to an sqlite <var>File</var>. If <var>Connection</var> is a variable, an opaque atom
is generated, otherwise the opened file is connected to handle Connecction.
<var>Options</var> is a sinlge term or a list of terms from the following:
<ul>
<li><code>alias(Atom)</code> identify the connection as Alias in all transactions</li>
<li><code>as_predicates(AsPred)</code> if true, create hook predicates that map
<center>each sqlite table to a prolog predicate.
These are created in module user, and it is
the user's responsibility to be unique in this module.</center>
</li>
<li><code>at_module(AtMod)</code> the module at which the predicates will be asserted at
(if <code>as_predicates(true)</code>) is also given). Default is <code>user</code>.</li>
<li><code>table_as(Table,Pname,Arity)</code> map the table to predicate with name Pname. Arity should be
defined for this representaition as per with_arity() option.</li>
<li><code>arity(arity)</code> Arity denotes the arity of access clauses to be added in the prolog database that
<center>correspond to SQLite tables. The default is <code>arity</code>, which asserts a
predicate matching the arity of the table.
<code>both</code> adds two predicates, one matching the arity and a single argument one.
The later can be interrogated with something like</center>
<pre class="code" ext=""> ?- phones( [name=naku, telephone=T] ).</pre>
<center><code>unary</code> only adds the unary version, and <code>palette</code> adds a suite of predicates
with arities from 1 to N, where N is the number of columns.
These can be interrogated by :</center>
<pre class="code" ext=""> ?- phones( name=Name ).
?- phones( name=naku, telephone=T ).
?- phones( [name=naku, telephone=T] ).</pre>
<center>Predicated tables can be used to insert values to the database by virtue of all
their columns are give ground values.</center>
</li>
<li><code>exists(Boolean)</code> do not throw an error if file does not exist and
<center>Boolean is false. Default is true and an error is
thrown if the Sqlite file does not exist.</center>
</li>
<li><code>ext(Ext)</code> database files are assumed to have an sqlite extension at their end.
To ovewrite this give Ext ('' for no extension).</li>
<li><code>verbose(Verb)</code> Iff Verb==true printa message about which file is used- from within C (false).</li>
</ul>
<p>When unary predicates are defined the columns can be interrogated/accessed by list pairs of the form Column=Value.
Column-Value and Column:Value are also recognised.</p>
<p>So for example, for table <code>phones</code> with columns Name, Address and Phone, prosqlite will add</p>
<pre class="code" ext=""> phones(_,_,_)</pre>
<pre class="code">as a response to as_predicates, and</pre>
<pre class="code" ext=""> phones(_)</pre>
<p>if Arity is <code>unary</code></p>
<p>The latter can be interrogated by</p>
<pre class="code" ext=""> phones( ['Name'=naku','Phone'=Phone] ).</pre>
<p>which will return the phone <code>number(s)</code> associated with individual named by <code>naku</code>.</p>
<p>See source file <a class="file" href="../examples/predicated.pl">examples/predicated.pl</a> .</p></dd>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_disconnect/1">
<b class="pred">sqlite_disconnect</b>
<var class="arglist">(+Alias)</var>
</a>
</dt>
<dd class="defbody">Terminate the connection to a SQLite database file.
<pre class="code" ext=""> sqlite_disconnect(uniprot).</pre>
</dd>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_current_connection/1">
<b class="pred">sqlite_current_connection</b>
<var class="arglist">(-Connection)</var>
</a>
</dt>
<dd class="defbody">Return or interrogate the name of open connection handles.</dd>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_default_connection/1">
<b class="pred">sqlite_default_connection</b>
<var class="arglist">(-Connection)</var>
</a>
</dt>
<dd class="defbody">Return or interrogate the name of the default connection. This is the
last connection opened.</dd>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_query/2">
<b class="pred">sqlite_query</b>
<var class="arglist">(+Sql, -Row)</var>
</a>
</dt>
<dd class="defbody">Post an <var>Sql</var> query to default connection and get row result in <var>Row</var>.</dd>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_query/3">
<b class="pred">sqlite_query</b>
<var class="arglist">(+Connection, +Sql, -Row)</var>
</a>
</dt>
<dd class="defbody">Post an <var>Sql</var> query to Sqlite <var>Connection</var> and get row result in <var>Row</var>.</dd>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_format_query/3">
<b class="pred">sqlite_format_query</b>
<var class="arglist">(+Connection, +FAs, -Row)</var>
</a>
</dt>
<dd class="defbody">Post a format style Sql query to Sqlite <var>Connection</var> and get row result in <var>Row</var>.
<var>FAs</var> is a - pair structure : Format-Arguments.
<pre class="code" ext=""> sqlite_format_query(uniprot, 'PRAGMA table_info(~w)'-Table, row(_, Column, _, _, _, _))</pre>
</dd>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_current_table/2">
<b class="pred">sqlite_current_table</b>
<var class="arglist">(+Connection, -Table)</var>
</a>
</dt>
<dd class="defbody">Return or interrogate tables in the Sqlite database associated with <var>Connection</var>.</dd>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_current_table/3">
<b class="pred">sqlite_current_table</b>
<var class="arglist">(+Connection, ?Table, -Facet)</var>
</a>
</dt>
<dd class="defbody"><var>Facet</var> is a property of <var>Table</var> found at <var>Connection</var>. Currently only <code>arity(Arity)</code> is
delivered.</dd>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_table_column/3">
<b class="pred">sqlite_table_column</b>
<var class="arglist">(+Connection, ?Table, -Column)</var>
</a>
</dt>
<dd class="defbody">Return or interrogate tables and columns in the Sqlite database associated with <var>Connection</var>.</dd>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_table_column/4">
<b class="pred">sqlite_table_column</b>
<var class="arglist">(+Connection, ?Table, ?Column, -Facet)</var>
</a>
</dt>
<dd class="defbody"><var>Facet</var> is one of:
<ul>
<li><code>position(Nth0)</code> position of the <var>Column</var> in the table, first being 0.</li>
<li><code>data_type(Dtype)</code> the data type for the column</li>
<li><code>nullable(Null)</code> can this column be set to the null value</li>
<li><code>defautl(Default)</code> the default value for the</li>
<li><code>primary_key(Key)</code> is this column part of the primary key ?</li>
</ul></dd>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_pragma/3">
<b class="pred">sqlite_pragma</b>
<var class="arglist">(+Alias, +Pragma, -Row)</var>
</a>
</dt>
<dd class="defbody">Interrogate SQLite Pragmas. Currently only reading is supported.
<var>Pragma</var> can be an atom or a - separated pair, as in <code>table_info-TableName</code>.
<pre class="code" ext=""> sqlite_pragma( phone_db, encoding, Row).</pre>
</dd>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_table_count/3">
<b class="pred">sqlite_table_count</b>
<var class="arglist">(+Connection, +Table, -Count)</var>
</a>
</dt>
<dd class="defbody">True if <var>Count</var> is the number of rows in Sqlite <var>Connection</var> associated <var>Table</var>.</dd>
<dt class="pubdef">
<span style="float:right"></span>
<a name="sqlite_date_sql_atom/2">
<b class="pred">sqlite_date_sql_atom</b>
<var class="arglist">(Date, Sql)</var>
</a>
</dt>
<dd class="defbody">Convert between a Prolog <a
href="../../../../../local/git/lib/swipl-6.3.19/library/http/http_header.pl#date/3">date/3</a> term and an <var>Sql</var> atom.
The conversion is bidirectional.</dd>
</dl>
</body>
</html>

View File

@ -0,0 +1,10 @@
name(prosqlite).
version('0.1.2').
title('An SWI-Prolog interface to SQLite').
keywords([sqlite,database,sql]).
author( 'Sander Canisius', 'http://bioinformatics.nki.nl/~canisius' ).
author( 'Nicos Angelopoulos', 'http://stoics.org.uk/~nicos' ).
packager( 'Nicos Angelopoulos', 'http://stoics.org.uk/~nicos' ).
maintainer( 'Nicos Angelopoulos', 'http://stoics.org.uk/~nicos' ).
home( 'http://stoics.org.uk/~nicos/sware/prosqlite' ).
download( 'http://stoics.org.uk/~nicos/sware/packs/prosqlite/prosqlite-*.tgz' ).

View File

@ -0,0 +1,764 @@
:- module( prosqlite,
[ sqlite_connect/2, % +FileName, -Conn
sqlite_connect/3, % +FileName, -Conn, +Opts
sqlite_disconnect/1, % +Conn
sqlite_current_connection/1,% -Conn
sqlite_query/2, % +SQL, -Row
sqlite_query/3, % +Conn, +SQL, -Row
sqlite_format_query/3, % +Conn, +SQL, -Row
sqlite_current_table/2, % +Conn, -Table
sqlite_current_table/3, % +Conn, ?Table, -Facet
sqlite_table_column/3, % +Conn, ?Table, ?Column
sqlite_table_column/4, % +Conn, ?Table, ?Column, -Facet
sqlite_table_count/3, % +Conn, +Table, -Count
sqlite_default_connection/1,% -Conn
sqlite_date_sql_atom/2, % ?Date, ?SqlAtom
sqlite_pragma/3, % ?Date, ?SqlAtom
sqlite_version/2, % -Version, -Date
sqlite_binary_version/2, % -Version, -Date
sqlite_citation/2 % -Atom, Bibterm
] ).
:- use_module(library(lists)).
% android does it by loading the code at startup.
:- if prolog_flag( host_type , Host), \+ sub_atom(Host, _, _, 0, androideabi).
:- use_module(library(shlib)).
:- load_foreign_library(foreign(prosqlite)).
:- endif.
:- dynamic( sqlite_connection/3 ).
:- dynamic( sqlite_db:sqlite_asserted/4 ).
/** <module> proSQLite: a Prolog interface to the SQLite database system.
This library follows the design and borrows code from the ODBC library of SWI-Prolog
http://www.swi-prolog.org/pldoc/packasqlite_connectge/odbc.html .
The SQLite system is a powerful zero-configuration management systme that interacts
with single-file databases that are cross-platform compatible binaries.
ProSQLite provides three layers of interaction with SQLite databases.
At the lower level is the querying via SQL statements. A second layer
allows the interogation of the database dictionary, and the final level
facilitates the viewing of database tables as predicates.
See the publication pointed to by sqlite_citation/2, for further details.
If you use prosqlite in your research, please consider citing this publication.
The library has been developed and tested on SWI 6.3.2 but it should
also work on YAP Prolog.
The easiest way to install on SWI is via the package manager.
Simply do:
==
?- pack_install( prosqlite ).
==
And you are good to go.
There are good/full examples in the sources, directory examples/.
For instance test by :
==
?- [predicated].
?- predicated.
==
There is a sister package, db_facts (also installable via the manager).
Db_facts, allow interaction with the underlying database via Prolog terms,
That library can also be used as a common compatibility layer for the ODBC
and proSQLite libraries of SWI-Prolog, as it works on both type of connections.
ProSQLite is debug/1 aware: call =|debug(sqlite)|= to see what is sent to
the sqlite engine.
There are MS wins DLLs included in the sources and recent version of the SWI package
manager will install these properly.
@version 0.1.2, 2013/11/1
@license Perl Artistic License
@author Nicos Angelopoulos
@author Sander Canisius
@see Sander Canisius, Nicos Angelopoulos and Lodewyk Wessels. proSQLite: Prolog file based databases via an SQLite interface. In the proceedings of Practical Aspects of Declarative languages (PADL 2013), (2013, Rome, Italy).
@see Sander Canisius, Nicos Angelopoulos and Lodewyk Wessels. Exploring file based databases via an Sqlite interface. In the ICLP Workshop on Logic-based methods in Programming Environments, p. 2-9, (2012, Budapest, Hungary).
@see http://stoics.org.uk/~nicos/pbs/wlpe2012_sqlite.pdf
@see http://stoics.org.uk/~nicos/sware/prosqlite
@see http://stoics.org.uk/~nicos/sware/db_facts
@see http://www.sqlite.org/
@see files in examples/ directory
@see also available as a SWI pack http://www.swi-prolog.org/pack/list
@tbd set pragmas
*/
:- use_module( library(debug) ).
:- at_halt( sqlite_disconnect ).
/* defaults and start ups */
arity_flag_values( [arity,unary,both,palette] ).
%-Section interface predicates
%
%% sqlite_version( -Version, -Date ).
% The current version. Version is a Mj:Mn:Fx term, and date is a date(Y,M,D) term.
%
sqlite_version( 0:1:2, date(2013,11,1) ).
%% sqlite_binary_version( -Version, -Date ).
% The current version of the binaries. If the installed binaries are not compiled from
% the sources, then this might be different (=older) that the sqlite Porlog source version
% returned by sqlite_version/2. Version is a Mj:Mn:Fx term, and date is a date(Y,M,D) term.
%
sqlite_binary_version( Ver, Date ) :-
c_sqlite_version( Ver, Date ).
%% sqlite_citation( -Atom, -Bibterm ).
% Succeeds once for each publication related to this library. Atom is the atom representation
% suitable for printing while Bibterm is a bibtex(Type,Key,Pairs) term of the same publication.
% Produces all related publications on backtracking.
sqlite_citation( Atom, bibtex(Type,Key,Pairs) ) :-
Atom = 'Sander Canisius, Nicos Angelopoulos and Lodewyk Wessels. proSQLite: Prolog file based databases via an SQLite interface. In the proceedings of Practical Aspects of Declarative languages (PADL 2013), (2013, Rome, Italy).',
Type = inproceedings,
Key = 'CanisiusS+2013',
Pairs = [
author = 'Sander Canisius and Nicos Angelopoulos and Lodewyk Wessels',
title = 'Exploring file based databases via an Sqlite interface',
booktitle = 'In ICLP Workshop on Logic-based methods in Programming Environments (WLPE\'12)',
year = 2012,
pages= '2-9',
month = 'September',
address = 'Budapest, Hungary',
url = 'http://stoics.org.uk/~nicos/pbs/padl2013-prosqlite.pdf'
].
sqlite_citation( Atom, bibtex(Type,Key,Pairs) ) :-
Atom = 'Exploring file based databases via an Sqlite interface. \n Canisius Sander, Nicos Angelopoulos and Lodewyk Wessels \n In the ICLP Workshop on Logic-based methods in Programming Environments (WLPE\'12),\n p.2-9, 2012. Budapest, Hungary.',
Type = inproceedings,
Key = 'CanisiusS+2012',
Pairs = [
author = 'Sander Canisius and Nicos Angelopoulos and Lodewyk Wessels',
title = 'Exploring file based databases via an Sqlite interface',
booktitle = 'In ICLP Workshop on Logic-based methods in Programming Environments (WLPE\'12)',
year = 2012,
pages= '2-9',
month = 'September',
address = 'Budapest, Hungary',
url = 'http://stoics.org.uk/~nicos/pbs/wlpe2012_sqlite.pdf'
].
%% sqlite_connect(+File, ?Alias).
%
% Open a connection to an sqlite File. If Alias is a variable, an opaque atom
% is generated and unified to it. The opened db connection to file can be accessed via Alias.
%
% ==
% sqlite_connect('uniprot.sqlite', uniprot).
% ==
%
sqlite_connect(File, Conn) :-
sqlite_connect(File, Conn, []).
/** sqlite_connect(+File, ?Connection, +Options).
Open a connection to an sqlite File. If Connection is a variable, an opaque atom
is generated, otherwise the opened file is connected to handle Connecction.
Options is a sinlge term or a list of terms from the following:
* alias(Atom) identify the connection as Alias in all transactions
* as_predicates(AsPred) if true, create hook predicates that map
each sqlite table to a prolog predicate.
These are created in module user, and it is
the user's responsibility to be unique in this module.
* at_module(AtMod) the module at which the predicates will be asserted at
(if as_predicates(true)) is also given). Default is =|user|=.
* table_as(Table,Pname,Arity) map the table to predicate with name Pname. Arity should be
defined for this representaition as per with_arity() option.
* arity(arity) Arity denotes the arity of access clauses to be added in the prolog database that
correspond to SQLite tables. The default is =|arity|=, which asserts a
predicate matching the arity of the table.
=|both|= adds two predicates, one matching the arity and a single argument one.
The later can be interrogated with something like
==
?- phones( [name=naku, telephone=T] ).
==
=|unary|= only adds the unary version, and =|palette|= adds a suite of predicates
with arities from 1 to N, where N is the number of columns.
These can be interrogated by :
==
?- phones( name=Name ).
?- phones( name=naku, telephone=T ).
?- phones( [name=naku, telephone=T] ).
==
Predicated tables can be used to insert values to the database by virtue of all
their columns are give ground values.
* exists(Boolean) do not throw an error if file does not exist and
Boolean is false. Default is true and an error is
thrown if the Sqlite file does not exist.
* ext(Ext) database files are assumed to have an sqlite extension at their end.
To ovewrite this give Ext ('' for no extension).
* verbose(Verb) Iff Verb==true printa message about which file is used- from within C (false).
When unary predicates are defined the columns can be interrogated/accessed by list pairs of the form Column=Value.
Column-Value and Column:Value are also recognised.
So for example, for table =|phones|= with columns Name, Address and Phone, prosqlite will add
==
phones(_,_,_)
==
as a response to as_predicates, and
==
phones(_)
==
if Arity is =|unary|=
The latter can be interrogated by
==
phones( ['Name'=naku','Phone'=Phone] ).
==
which will return the phone number(s) associated with individual named by =|naku|=.
See source file examples/predicated.pl .
*/
sqlite_connect(FileIn, Conn, OptIn) :-
to_list( OptIn, Opts ),
( memberchk(ext(Ext),Opts) -> true; Ext=sqlite ),
( file_name_extension(_,Ext,FileIn) -> File=FileIn; file_name_extension(FileIn,Ext,File) ),
sqlite_connect_1(File, Conn, Opts).
sqlite_connect_1(File, _Conn, Opts) :-
\+ exists_file(File),
\+ memberchk(exists(false), Opts),
!,
open(File, read, _). % just so it throws a nice error
sqlite_connect_1(File1, Conn, Opts) :-
sqlite_alias(Opts, Conn, Alias),
\+ var(Alias),
sqlite_connection(Conn,File2,_),
!,
( File1==File2 ->
print_message( informational, sqlite(connection_already_open(Conn)) )
;
sqlite_error( alias_in_use(Conn,File2) )
).
sqlite_connect_1(File, Alias, Opts) :-
sqlite_alias(Opts, Conn, Alias),
( sqlite_connection(_Conn1,File,Alias1) ->
portray_message( informational, file_already_open(File,Alias1) )
;
true
),
( (memberchk(verbose(Verb),Opts),Verb==true) ->
print_message( informational, sqlite(db_at(File)) )
;
true
),
c_sqlite_connect(File, Conn),
asserta( sqlite_connection(Alias,File,Conn) ),
( sqlite_establish_predicates(Opts, Conn) ->
true
;
retractall( sqlite_connection(Alias,File,Conn) ),
c_sqlite_disconnect(Conn),
sqlite_error( predicated_creation_error(File,Alias) )
).
/*
sqlite_connect(File, Conn, Opts) :-
c_sqlite_connect(File, Internal),
!,
assert( sqlite_connection(Conn,File,Internal) ).
*/
% this is currently private only for use with at_halt.
%
sqlite_disconnect :-
sqlite_connection(Alias,_,_),
sqlite_disconnect( Alias ),
fail.
sqlite_disconnect.
%% sqlite_disconnect( +Alias ).
%
% Terminate the connection to a SQLite database file.
%
% ==
% sqlite_disconnect(uniprot).
% ==
%
sqlite_disconnect( Alias ) :-
once( sqlite_connection(Alias,_,Conn) ),
!,
debug( sqlite, 'Disconnecting from db with alias: ~w.', [Alias] ),
c_sqlite_disconnect( Conn ),
retractall( sqlite_connection(Alias,_,Conn) ),
findall( pam(Pname,Arity,Mod), sqlite_db:sqlite_asserted(Conn,Pname,Arity,Mod), PAs ),
maplist( sqlite_clean_up_predicated_for(Conn), PAs ).
sqlite_disconnect( Alias ) :-
sqlite_fail( not_a_connection(Alias) ).
sqlite_clean_up_predicated_for( Conn, pam(Pname,Arity,Mod) ) :-
% functor( Head, Pname, Arity ),
% retractall( Mod:Head ),
abolish( Mod:Pname, Arity ),
retractall( sqlite_db:sqlite_asserted(Conn,Pname,Arity,Mod) ).
%% sqlite_current_connection(-Connection).
%
% Return or interrogate the name of open connection handles.
%
sqlite_current_connection(Conn) :-
sqlite_connection(Conn,_,_).
%% sqlite_default_connection(-Connection).
%
% Return or interrogate the name of the default connection. This is the
% last connection opened.
%
sqlite_default_connection(Alias) :-
sqlite_connection(Alias,_,_),
!.
%% sqlite_query(+Sql, -Row).
%
% Post an Sql query to default connection and get row result in Row.
%
sqlite_query(Sql, Row) :-
sqlite_default_connection(Alias),
sqlite_query(Alias, Sql, Row).
%% sqlite_query(+Connection, +Sql, -Row).
%
% Post an Sql query to Sqlite Connection and get row result in Row.
%
sqlite_query(Alias, Query, Row) :-
sqlite_alias_connection(Alias, Connection),
debug( sqlite, 'Alias: ~w, sending: ~a', [Alias,Query] ),
c_sqlite_query(Connection, Query, Row).
%% sqlite_format_query(+Connection, +FAs, -Row).
%
% Post a format style Sql query to Sqlite Connection and get row result in Row.
% FAs is a - pair structure : Format-Arguments.
%
% ==
% sqlite_format_query(uniprot, 'PRAGMA table_info(~w)'-Table, row(_, Column, _, _, _, _))
% ==
%
%
sqlite_format_query(Alias, Format-Arguments, Row) :-
format(atom(Query), Format, Arguments),
sqlite_query(Alias, Query, Row).
%% sqlite_current_table(+Connection, -Table).
%
% Return or interrogate tables in the Sqlite database associated with Connection.
%
sqlite_current_table(Alias, Table) :-
var( Table ),
!,
sqlite_query(Alias, 'SELECT name FROM sqlite_master WHERE type = "table"', row(Table)).
sqlite_current_table(Alias, Table) :-
ground( Table ),
sqlite_query(Alias, 'SELECT name FROM sqlite_master WHERE type = "table"', row(TableIn)),
%13.10.26: have a look at the C code above to see if 'row(Table)' can work on the above line.
Table = TableIn,
!.
%% sqlite_current_table(+Connection, ?Table, -Facet ).
%
% Facet is a property of Table found at Connection. Currently only arity(Arity) is
% delivered.
sqlite_current_table(Connection, Table, Facet ) :-
sqlite_current_table(Connection, Table),
sqlite_facet_table( Facet, Connection, Table ).
%% sqlite_table_column(+Connection, ?Table, -Column).
%
% Return or interrogate tables and columns in the Sqlite database associated with Connection.
%
sqlite_table_column( Alias, Table, Column ) :-
set_table( Alias, Table ),
sqlite_format_query(Alias, 'PRAGMA table_info(~w)'-Table, row(_, Column, _, _, _, _)).
%% sqlite_table_column(+Connection, ?Table, ?Column, -Facet).
%
% Facet is one of:
% * position(Nth0) position of the Column in the table, first being 0.
% * data_type(Dtype) the data type for the column
% * nullable(Null) can this column be set to the null value
% * defautl(Default) the default value for the
% * primary_key(Key) is this column part of the primary key ?
%
sqlite_table_column(Alias, Table, Column, Facet) :-
set_table( Alias, Table ),
sqlite_format_query(Alias, 'PRAGMA table_info(~w)'-Table, Row ),
Row = row(_, Column, _, _, _, _),
sqlite_pragma_info_facet( Row, Facet ).
sqlite_pragma_info_facet( row(Nth0,_,_,_,_,_), position(Nth0) ).
sqlite_pragma_info_facet( row(_,_,Dtype,_,_,_), data_type(Dtype) ).
sqlite_pragma_info_facet( row(_,_,_,Null,_,_), nullable(Null) ). % fixme, ensure same semantics as ODBC
sqlite_pragma_info_facet( row(_,_,_,_,Default,_), default(Default) ).
sqlite_pragma_info_facet( row(_,_,_,_,_,Key), primary_key(Key) ).
%% sqlite_pragma( +Alias, +Pragma, -Row ).
%
% Interrogate SQLite Pragmas. Currently only reading is supported.
% Pragma can be an atom or a - separated pair, as in =|table_info-TableName|=.
%
%==
% sqlite_pragma( phone_db, encoding, Row).
%==
sqlite_pragma( Alias, Pragma-Par, Row ) :-
!,
atomic_list_concat( ['PRAGMA',Pragma,'(~w)'],' ', Query ),
sqlite_format_query( Alias, Query-Par, Row ).
sqlite_pragma( Alias, Pragma, Row ) :-
atomic_list_concat( ['PRAGMA',Pragma],' ', Query ),
sqlite_query( Alias, Query, Row ).
% pragmas_info( [...,encoding,...,secure_delete,synchronous,temp_store,writable_schema] ).
pragmas_comm( [shrink_memory] ).
set_table( Alias, Table ) :-
( var(Table) ->
sqlite_current_table(Alias, Table)
;
true
).
%% sqlite_table_count(+Connection, +Table, -Count).
%
% True if Count is the number of rows in Sqlite Connection associated Table.
%
sqlite_table_count(Alias, Table, Count) :-
Sel = 'Select count (*) from ~w',
sqlite_format_query(Alias, Sel-Table, row(Count)),
!.
/** sqlite_date_sql_atom( Date, Sql ).
Convert between a Prolog date/3 term and an Sql atom.
The conversion is bidirectional.
*/
sqlite_date_sql_atom( date(Y,M,D), Sql ) :-
ground( Sql ),
!,
atomic_list_concat( Consts, '/', Sql ),
maplist( atom_number, Consts, [Y,M,D] ).
sqlite_date_sql_atom( date(Y,M,D), Sql ) :-
atomic_list_concat( ['"',Y], Ya ),
atomic_list_concat( [D,'"'], Da ),
atomic_list_concat( [Ya,M,Da], '/', Sql ).
%-Section non-interface sqlite specific predicates
%
sqlite_alias(Opts, _Conn, Alias) :-
memberchk(alias(Alias), Opts),
!.
sqlite_alias(_Opts, _Conn, Alias ) :-
atomic( Alias ),
!.
sqlite_alias(_Opts, Conn, Conn).
sqlite_establish_predicates( Opts, Conn ) :-
memberchk(as_predicates(true), Opts),
!,
findall(T-C, sqlite_table_column(Conn,T,C), TCs ),
findall( T, member(T-_,TCs), RepTs ),
sort( RepTs, Ts ),
findall( T-Cs, (member(T,Ts),findall(C,member(T-C,TCs),Cs)), TdCs ),
( memberchk(at_module(Mod), Opts) -> true; Mod = user ),
arity_option( Opts, ArityF ),
sqlite_establish_tables(TdCs, Conn, Mod, ArityF, Opts ).
sqlite_establish_predicates(_Opts, _Conn ).
sqlite_establish_tables( [], _Conn, _Mod, _ArityF, _Opts ).
sqlite_establish_tables( [Table-Columns|T], Conn, Mod, ArityF, Opts ) :-
( memberchk(table_as(Table,Pname,TArityF), Opts) ->
true
;
Pname = Table, TArityF = ArityF
),
sqlite_establish_table(TArityF,Table,Pname,Columns,Conn,Mod),
% Internal = 'Internal prosqlite error. Unable to establish table',
% throw( Internal:TArityF/Table ) % handled furter up now
sqlite_establish_tables( T, Conn, Mod, ArityF, Opts ).
sqlite_establish_table( arity, Table, Pname, Columns, Conn, Mod ) :-
length( Columns, Arity ),
sqlite_establish_table_typed( Table, Pname, Columns, Conn, Mod, predicate, Arity ).
sqlite_establish_table( both, Table, Pname, Columns, Conn, Mod ) :-
sqlite_establish_table_typed( Table, Pname, Columns, Conn, Mod, unary, 1 ),
length( Columns, Arity ),
sqlite_establish_table_typed( Table, Pname, Columns, Conn, Mod, predicate, Arity ).
sqlite_establish_table( unary, Table, Pname, Columns, Conn, Mod ) :-
sqlite_establish_table_typed( Table, Pname, Columns, Conn, Mod, unary, 1 ).
sqlite_establish_table( palette, Table, Pname, Columns, Conn, Mod ) :-
length( Columns, Arity ),
% Shorter is Arity - 1,
findall( _, ( between(1,Arity,I),
sqlite_establish_table_typed(Table, Pname, Columns, Conn, Mod, palette, I)
), _ ).
sqlite_establish_table_typed( Table, Pname, Columns, Conn, Mod, ArityF, Arity ) :-
functor( Head, Pname, Arity ),
Head =..[Pname|Args],
Body = prosqlite:sqlite_holds(Conn,Table,Arity,ArityF,Columns,Args),
( clause(Mod:Head,_Body) ->
sqlite_fail( maps_to_existing_predicate(Pname,Arity) )
;
true
),
% retractall( Mod:Head ), % fixme: double check this and test it works
( sqlite_db:sqlite_asserted(Conn1,Pname,Args,_Mod1) ->
sqlite_fail( predicate_already_registered(Conn1,Pname,Arity) )
;
Mod:assert((Head :- Body))
),
assert( sqlite_db:sqlite_asserted(Conn,Pname,Arity,Mod) ).
% assert((Head :- Body)),
sqlite_holds( AliasOr, Name, _Arity, Type, Columns, Args ) :-
sqlite_alias_connection( AliasOr, Conn ),
pl_as_predicate_to_sql_ready_data( Type, Columns, Args, KnwnClmPrs, UnKnwnCs, UnKnwnAs ),
sqlite_holds_unknown( UnKnwnCs, UnKnwnAs, KnwnClmPrs, Name, Columns, Conn ).
/* fixme:
sqlite_holds_unknown( [], _UnKnwnAs, KnwnClmPrs, Name, Columns, Conn ) :-
shall we throw an error if there is nothing to report and nothing to assert ?
*/
sqlite_holds_unknown( UnKnwnCs, UnKnwnAs, KnwnClmPrs, Name, _Columns, Conn ) :-
sql_clm_value_pairs_to_where(KnwnClmPrs, Where),
atomic_list_concat( UnKnwnCs, ',', UnC ),
atomic_list_concat( ['Select ',UnC,'From',Name,Where,';'], ' ', Sql ),
Row =.. [row|UnKnwnAs],
debug( sqlite, 'Conn: ~w, sending: ~a', [Conn,Sql] ),
c_sqlite_query(Conn, Sql, Row).
sqlite_alias_connection( Alias, Connection ) :-
sqlite_connection( Alias,_,Connection ),
!.
% allows access with either alias or connection :
sqlite_alias_connection( Connection, Connection ) :-
sqlite_connection(_,_,Connection),
!.
sqlite_alias_connection( Alias, _Connection ) :-
sqlite_error( unknown_alias(Alias) ).
% fixme: we should really use the db_facts code here.
pl_as_predicate_to_sql_ready_data( unary, Columns, [Args], KnwnClmPrs, UnKnwnCs, UnKnwnAs ) :-
pl_look_for_args_to_un_known( Args, Columns, KnwnClmPrs, UnKnwnCs, UnKnwnAs ).
pl_as_predicate_to_sql_ready_data( palette, Columns, ArgsIn, KnwnClmPrs, UnKnwnCs, UnKnwnAs ) :-
( (ArgsIn=[Args],is_list(Args)) -> true; Args = ArgsIn ),
pl_args_column_arg_ground_or_not_pairs(Args,Columns,KnwnClmPrs,UnKnwnCs,UnKnwnAs),
( maplist(var,Args) ->
true % then a palette predicate has been called with full arity and all variables
;
% maplist( look_for_pair,Args,_,_),
findall( LFA, (member(LFA,Args),look_for_pair_silent(LFA,_,_)), [] )
% then a palette predicate has been called with full arity and look_for_pair
),
!.
pl_as_predicate_to_sql_ready_data( palette, Columns, ArgsIn, KnwnClmPrs, UnKnwnCs, UnKnwnAs ) :-
( (ArgsIn=[Args],is_list(Args)) -> true; Args = ArgsIn ),
pl_look_for_args_to_un_known( Args, Columns, KnwnClmPrs, UnKnwnCs, UnKnwnAs ).
pl_as_predicate_to_sql_ready_data( predicate, Columns, Args, KnwnClmPrs, UnKnwnCs, UnKnwnAs ) :-
pl_args_column_arg_ground_or_not_pairs( Args, Columns, KnwnClmPrs, UnKnwnCs, UnKnwnAs ).
pl_args_column_arg_ground_or_not_pairs( [], [], [], [], [] ).
pl_args_column_arg_ground_or_not_pairs( [A|As], [C|Cs], Knwn, UnCs, UnAs ) :-
( ground(A) ->
Knwn = [C-A|TKnwn],
TUnCs = UnCs,
TUnAs = UnAs
;
TKnwn = Knwn,
UnCs = [C|TUnCs],
UnAs = [A|TUnAs]
),
pl_args_column_arg_ground_or_not_pairs( As, Cs, TKnwn, TUnCs, TUnAs ).
pl_look_for_args_to_un_known( [], _Columns, [], [], [] ).
pl_look_for_args_to_un_known( [A|As], Columns, Knwn, UnKnwnCs, UnKnownAs ) :-
look_for_pair( A, Clm, Val ),
is_one_of_columns( Clm, Columns ),
( ground(Val) ->
Knwn = [Clm-Val|TKnwn],
TUnKnwnCs = UnKnwnCs,
TUnKnownAs = UnKnownAs
;
TKnwn = Knwn,
UnKnwnCs = [Clm|TUnKnwnCs],
UnKnownAs = [Val|TUnKnownAs]
),
pl_look_for_args_to_un_known( As, Columns, TKnwn, TUnKnwnCs, TUnKnownAs ).
is_one_of_columns( Clm, Columns ) :-
memberchk( Clm, Columns ),
!.
is_one_of_columns( Clm, Columns ) :-
sqlite_error( unknown_column(Clm,Columns) ).
look_for_pair( Pair, K, V ) :-
look_for_pair_silent( Pair, K, V ),
!.
look_for_pair( Term, _A, _B ) :-
% print_message(informational, pack(git_fetch(Dir))).
sqlite_error( pair_representation(Term) ).
% type_error( 'Binary compound with functor {=,-,:}', Term ).
% Type = 'Binary compound with functor {=,-,:}',
% print_message( error, error(type_error(Type,Term)) ),
% abort.
look_for_pair_silent( A=B, A, B ).
look_for_pair_silent( A-B, A, B ).
look_for_pair_silent( A:B, A, B ).
/* error messages */
sqlite_error( Term ) :-
Type = error,
print_message( Type, sqlite(Term) ),
abort.
sqlite_fail( Term ) :-
Type = informational,
sqlite_fail( Type, Term ).
sqlite_fail( Type, Term ) :-
print_message( Type, sqlite(Term) ),
fail.
%-Section error handling.
:- multifile prolog:message//1.
prolog:message(sqlite(Message)) -->
message(Message).
message( pair_representation(Term) ) -->
['Wrong term type ~q in predicated table arguments. Expected binary with functor, {=,:,-}.' - [Term] ].
message( unknown_column(Clm,Columns) ) -->
[ 'Unkown column, ~q expected one in ~q.' - [Clm,Columns] ].
message( unknown_alias(Alias) ) -->
['Not a known alias or connection:~q.' - Alias ].
message( wrong_arity_value(ArityF) ) -->
{ arity_flag_values( Arities ) },
[ 'Unrecognised arity option value ~q, expected ~q.' - [ArityF,Arities] ].
message( predicated_creation_error(File,Alias) ) -->
[ 'Closed connection ~q to file ~q due to failure in predicated table creation.' - [Alias,File] ].
message( connection_already_open(Conn) ) -->
[ 'Connection already open ~q.'- [Conn] ].
message( alias_in_use(Conn,File) ) -->
[ 'Alias/connection ~q already in use for file ~q.'- [Conn,File] ].
message( not_a_connection(Alias) ) -->
[ 'Not an open connection or known alias to a connection: ~q' - [Alias] ].
message( insufficient_columns(Goal,Op) ) -->
[ 'Insufficient number of known column values in ~q for operation ~q.' - [Goal,Op] ].
message( predicate_already_registered(Conn,Pname,Arity) ) -->
[ 'Predicate ~q already registered by connection ~q' - [Pname/Arity,Conn] ].
message( maps_to_existing_predicate(Pname,Arity) ) -->
['Predicated table maps to existing predicate ~q.' - [Pname/Arity] ].
message( file_already_open(File,Alias) ) -->
['File, ~q already open with alias ~q.' - [File,Alias] ].
message( db_at(File) ) -->
['Using database from file: ~q.' - [File] ].
message( asserting_non_ground(Goal) ) -->
[ 'Asserting non ground term ~q.' - [Goal] ].
message( debug(Format,Args) ) -->
[ 'Found Format (1st arg) ~q and Args (2nd arg) ~q.' - [Format,Args] ].
%-Section sqlite non-specific auxiliary predicates
%
to_list(OptIn, Opts) :-
is_list(OptIn),
!,
Opts = OptIn.
to_list(Opt, [Opt] ).
dquote( Val, Quoted ) :-
number( Val ),
!,
Quoted = Val.
dquote( Val, Quoted ) :-
atom( Val ),
!,
atomic_list_concat( ['"',Val,'"'], Quoted ).
dquote( Val, Quoted ) :-
is_list( Val ),
append( [0'"|Val], [0'"], QuotedCs ),
atom_codes( Quoted, QuotedCs ).
sql_clm_value_pairs_to_where(Known, Where) :-
sql_clm_value_pairs_to_where_conjunction(Known, Conjunction),
sql_where_conjunction_to_where(Conjunction, Where).
sql_where_conjunction_to_where('', '' ) :- !.
sql_where_conjunction_to_where(Conjunction, Where ) :-
atom_concat( 'Where ', Conjunction, Where ).
sql_clm_value_pairs_to_where_conjunction([], '').
sql_clm_value_pairs_to_where_conjunction([K-V|T], Where) :-
sql_clm_value_pairs_to_where_conjunction(T, InWhere),
sql_clm_and_val_to_sql_equals_atom(K, V, KVAtm),
( InWhere == '' ->
Where = KVAtm
;
atomic_list_concat([KVAtm, ' AND ', InWhere], Where)
).
sql_clm_and_val_to_sql_equals_atom(K, V, KVAtm) :-
( number(V) ->
atom_number(Vatm, V),
atom_concat('=',Vatm,EqV)
;
atom_concat(V, '\'', VDsh),
atom_concat('=\'',VDsh,EqV)
),
atom_concat(K, EqV, KVAtm).
sqlite_facet_table( arity(Arity), Connection, Table ) :-
findall( Column, sqlite_table_column(Connection, Table, Column), Columns ),
length( Columns, Arity ).
arity_option( Opts, ArityF ) :-
memberchk( arity(ArityF), Opts ),
arity_flag_values( Arities ),
memberchk( ArityF, Arities ),
!.
arity_option( Opts, ArityF ) :-
memberchk( arity(ArityF), Opts ),
!,
sqlite_fail( wrong_arity_value(ArityF) ).
arity_option( _Opts, arity ). % default for this flag, although we should
% move all defaults to one location/list (fixme)
kv_decompose( [], [], [] ).
kv_decompose( [K-V|T], [K|Ks], [V|Vs] ) :-
kv_decompose( T, Ks, Vs ).