765 lines
29 KiB
Prolog
765 lines
29 KiB
Prolog
:- 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 ).
|