:- module(dbi).
:- export dbi_error_handler / 2.
:- export cursor_field_value / 3.
:- export session_retrieve_lazy_tuples / 5.
:- export session_retrieve_N_tuples / 5.
:- export session_retrieve_tuple / 4.
:- export session_sql_prepare_query_N / 6.
:- export session_sql_prepare_query / 5.
:- export cursor_all_tuples / 2.
:- export cursor_N_tuples / 4.
:- export cursor_next_tuple / 2.
:- export session_sql_query_N / 6.
:- export session_sql_query / 5.
:- export session_sql_query / 4.
:- export cursor_close / 1.
:- export cursor_all_execute / 2.
:- export cursor_N_execute / 4.
:- export cursor_next_execute / 3.
:- export cursor_next_execute / 2.
:- export session_N_sql_prepare / 5.
:- export session_sql_prepare / 4.
:- export session_sql / 3.
:- export session_transaction / 3.
:- export session_rollback / 1.
:- export session_commit / 1.
:- export session_error_value / 3.
:- export session_close / 1.
:- export session_start / 3.
:- export session_start / 4.
:- export session_transaction / 2.
:- export struct(cursor(handle, session)).
:- tool(session_transaction / 2,session_transaction / 3).
:- comment(session_transaction / 3, hidden).
:- comment(summary, "Interface to MySQL databases").
:- comment(author, "Kish Shen, based on Oracle interface by Stefano Novello").
:- comment(date, "$Date: 2007/07/03 16:24:31 $").
:- comment(copyright, "Cisco Systems, 2006").
:- comment(desc, html("<P>\n This library provides an interface to database management systems (DBMS)\n that supports SQL (Structure Query Language), allowing SQL commands,\n written in an ECLiPSe program, to be passed to the DBMS for processing,\n and their results (if any) to be passed back to ECLiPSe.\n</P><P>\n The exact SQL supported by the library is determined by the underlying\n DBMS, as the SQL commands are passed directly to the DBMS via its C API. \n If supported by the C API, the library also allows the use of prepared SQL\n statements, where a SQL statement, possibly parameterised to accept\n different input data, is parsed once by the DBMS, and efficiently executed\n multiple times. Support for transactions (with commit and rollback) is also\n provided.\n</P><P>\n The library provides a relatively low-level interface to the DBMS, but\n higher level interfaces (e.g. where a SQL query is viewed as a predicate,\n yielding different results tuples on backtracking) can be constructed on\n top of this.\n</P><P>\n Currently, MySQL (version 5.0 or later) is supported by the library.\n Note that the MySQL client dynamic library (libmysqlclient.so on Unix\n systems, mysql.dll on Windows) is not included with the distribution, and\n if this file is not found by ECLiPSe on loading lib(dbi), there will be \n an error. This file can be obtained by downloading MySQL, and placing the\n .so or .dll file into a place where ECLiPSe can find it, for example a \n standard system library location, or in the ECLiPSe library location \n (<eclipsedir>/lib/<arch>).\n \n</P><P>\n Data is exchanged with the DBMS via:\n<DL>\n<P>\n<DT>SQL statements (input)\n  <DD>Directly in the SQL statements passed to the DBMS;\n<P>\n<DT>SQL query results (output)\n  <DD>The results returned by SQL queries are returned to ECLiPSe via\n      result tuples. Each tuple represents a single row of result, and is\n      returned to ECLiPSe as a Prolog structure, with each argument\n      represents one column in the result tuple. The ordering of the\n      arguments corresponds to the ordering defined by the SQL query.\n<DT>Parameters for prepared SQL statements (input)\n  <DD>If prepared statements are supported by the DBMS, data can also be\n      passed to the DBMS via parameters (also known as placeholders). Each\n      parameter functions like a variable, whose value is instantiated when\n      the statement is executed. The library passes parameter values to the\n      DBMS via Prolog structures, with each argument represent one parameter. \n      The ordering of the arguments is defined by the ordering of the\n      parameters in the prepared SQL statement.\n </DL>\n<P>\n Input and output data in the structures need to be converted from/to\n ECLiPSe datatypes. The ECLiPSe datatype is specified by a template, which\n specifies the datatype by example, i.e. an example result tuple structure\n (for output) or parameter structure (for input) where each argument\n specify the type for that argument, e.g.\n    <TT>emp(123,\"some name\",100.0,'some job',extra(data))</TT>,\n specifies that the first argument should be an integer, the second a \n string, the third a real, the fourth, an atom, and the last, a general\n Prolog term. Note that general Prolog terms are stored in the database in\n a binary representation, and the conversion is done by the library. \n</P><P>\n The data is exchanged with the DBMS via buffers. Some DBMS require the\n sizes of the buffers to be specified. This is done automatically by the\n library, but if the default sizes need to overridden (e.g. if the argument\n is large), the template can be used to specify the size of buffers for\n variable length data, e.g.\n    <TT>emp(123,\"1000\",100.0,'2000',extra(5000))</TT>,\n where the second argument <TT>\"1000\"</TT> specifies a buffer size of 1000\n bytes, the third argument <TT>'2000'</TT> a buffer size of 2000 bytes,\n and the last argument, <TT>extra(5000)</TT>, a buffer size of 5000 bytes.\n Note that any size specifications are simply ignored if not required.\n")).
:- comment(cursor_N_execute / 4, [amode : cursor_N_execute(++, -, +, -), args : ["Cursor" : "A cursor handle", "Executed" : "Number of times the SQL statement was executed.", "TupleList" : "A difference list (together with RestTupleList) of tuples of parameter values matching the template for this cursor", "RestTupleList" : "Tail of tuple difference list"], summary : "Executes the parametrised prepared SQL statement represented by Cursor, once for each tuple in TupleList.", see_also : [cursor_next_execute / 2, cursor_N_execute / 4, session_sql_prepare / 4, session_sql_prepare_query / 5], exceptions : [5 : "Cursor is not a valid cursor handle", 5 : "Type mismatch between parameter template specification for Cursor and actual tuple data", dbi_error : "Error from DBMS while executing SQL associated with Cursor.", dbi_bad_template : "ParamTemplate not specified when Cursor was created"], eg : "\n  transfer_(Session, Amount, FromAccount, ToAccount) :-\n      SQL = \"update accounts set balance = balance + ?                                                where id = ?\",\n      Deduct is - Amount,\n      session_sql_prepare(Session,incbal(1.0,12),SQL,2,Update),\n      Updates = [incbal(Deduct,FromAccount),incbal(Amount,ToAccount)],\n      % execute both updates with one call to cursor_N_execute/4\n      cursor_N_execute(Update,2,Updates,[]).\n", desc : html("<P>\n Executes the parameterised prepared SQL statement represented by Cursor,\n previously prepared by session_sql_prepare/4 or session_sql_prepare_query/5.\n The predicate executes a number of tuples from TupleList, taking the\n tuples in order from the start of the list, leaving the unprocessed tuples\n in RestTupleList. The number of tuple processed is unified with Executed.\n Any results produced when executing a tuple is discarded when the next\n tuple is processed.\n</P><P>\n The number of tuples processed per call is determined by the library. \n This freedom allows the  predicate to process a multiple number of tuples\n in the most efficient way for the DBMS.\n</P><P>\n Each tuple is a structure whose name and arity match the parameter\n template when Cursor was prepared, and the arguments give the values for\n the parameters for this execution, and must be compatible with the type\n specified by the template, except that an argument can be an\n uninstantiated variable, to denote a NULL value for the corresponding\n parameter.\n</P><P>\n MySQL specific note: MySQL does not support the execution of multiple\n tuples in one go, so for MySQL, this predicate is essentially equivalent\n to executing cursor_next_execute/1 multiple times.\n")]).
:- comment(cursor_N_tuples / 4, [amode : cursor_N_tuples(++, -, -, -), args : ["Cursor" : "A cursor handle", "Retrieved" : "Number of result tuples retrieved", "ResultTuples" : "Returned difference list (with RestResultTuples) of result tuples", "RestResultTuples" : "Tail of returned result tuples"], summary : "Retrieve  result tuples from the SQL query in the difference list ResultTuples and RestResultTuples.", see_also : [session_sql_query / 4, session_sql_prepare_query / 5, cursor_next_tuple / 2, cursor_all_tuples / 2], exceptions : [5 : "Cursor is not a valid cursor handle", 5 : "Unable to convert tuple result to ECLiPSe type", dbi_error : "Error from DBMS while executing SQL associated with Cursor.", dbi_error : "Error from DBMS while fetching result", dbi_not_query : "The SQL associated with Cursor is not a query and so cannot return results."], desc : html("<P>\n This is similar to cursor_all_tuples/4 except it works on difference list.\n It is designed to efficiently retrieve a buffer full of tuples for DBMS\n that support the retrieval of multiple tuples in its API. Otherwise, all\n the remaining tuples are retrieved one by one and the ResultTuples list\n constructed. Retrieved is unified with the number of retrieved result\n tuples. If no tuples match the query, Retrieved will be 0. Each element\n of the list is a single result tuple. Cursor is a cursor previously\n created with session_sql_query/4 or session_sql_prepare_query/5. Each\n tuple in ResultTuples is a structure with the same name and arity as\n defined by the tuple template when the cursor was created. The predicate\n converts the result to the type specified in the template, except that\n NULL values are returned as variables.\n</P><P>\n If the SQL query have not yet been executed, and it contains no\n parameters, then the SQL query will first be executed before retrieving\n the results.\n</P><P>\n MySQL specific note: MySQL does not support the retrieval of multiple\n tuples in one go, so for MySQL, this predicate is essentially equivalent\n to retrieving the results one by one.\n")]).
:- comment(cursor_all_execute / 2, [amode : cursor_all_execute(++, +), args : ["Cursor" : "A cursor handle", "TupleList" : "A list of tuples of parameter values matching the template for this cursor"], summary : "Executes the parametrised prepared SQL statement represented by Cursor, once for each tuple in TupleList.", see_also : [cursor_next_execute / 2, cursor_N_execute / 4, session_sql_prepare / 4, session_sql_prepare_query / 5], exceptions : [5 : "Cursor is not a valid cursor handle", 5 : "Type mismatch between parameter template specification for Cursor and actual tuple data", dbi_error : "Error from DBMS while executing SQL associated with Cursor.", dbi_bad_template : "ParamTemplate not specified when Cursor was created"], desc : html("<P>\n Executes the parameterised prepared SQL statement represented by Cursor,\n previously prepared by session_sql_prepare/4 or session_sql_prepare_query/5.\n The statement is executed once for each tuple in TupleList, with the \n parameter values supplied by that tuple. The tuples are executed in the\n order they are in TupleList, and any results produced are discarded when\n the next tuple is executed.\n</P><P>\n Each tuple is a structure whose name and arity match the parameter\n template when Cursor was prepared, and the arguments give the values for\n the parameters for this execution, and must be compatible with the type\n specified by the template, except that an argument can be an\n uninstantiated variable, to denote a NULL value for the corresponding\n parameter.")]).
:- comment(cursor_all_tuples / 2, [amode : cursor_all_tuples(++, -), args : ["Cursor" : "A cursor handle", "ResultTuples" : "Returned list of result tuples"], summary : "Retrieve all remaining result tuples from the SQL query in ResultTuples", see_also : [session_sql_query / 4, session_sql_prepare_query / 5, cursor_next_tuple / 2, cursor_N_tuples / 4], exceptions : [5 : "Cursor is not a valid cursor handle", 5 : "Unable to convert tuple result to ECLiPSe type", dbi_error : "Error from DBMS while executing SQL associated with Cursor.", dbi_error : "Error from DBMS while fetching result", dbi_not_query : "The SQL associated with Cursor is not a query and so cannot return results."], desc : html("<P>\n Retrieve the all remaining result tuples from the SQL query represented by\n Cursor, and unify them with ResultTuples list. Each element of the list is\n a single result tuple. Cursor is a cursor previously created with\n session_sql_query/4 or session_sql_prepare_query/5. Each tuple in\n ResultTuples is a structure with the same name and arity as defined by the\n tuple template when the cursor was created. The predicate converts the\n result to the type specified in the template, except that NULL values are\n returned as variables.\n</P><P>\n If the SQL query have not yet been executed, and it contains no\n parameters, then the SQL query will first be executed before retrieving\n the results.")]).
:- comment(cursor_close / 1, [amode : cursor_close(++), args : ["Cursor" : "A cursor handle"], summary : "Close the cursor associated with Cursor", exceptions : [5 : "Cursor is not a cursor handle."], see_also : [session_close / 1, session_sql_prepare / 4, session_sql_query / 4, session_sql_prepare_query / 5], desc : html("<P>\n Closes a cursor explicitly, and frees the resources associated with the\n cursor. It takes effect immediately.\n</P><P>\n It is recommended that the user close all cursors before explicitly\n closing a session. Cursors that remain open after a session is closed\n cannot be used, but may still be using resources.\n</P><P>\n Cursors are automatically closed if the program backtracks or aborts back\n beyond where they were created.\n")]).
:- comment(cursor_field_value / 3, hidden).
:- comment(cursor_next_execute / 2, [amode : cursor_next_execute(++, +), args : ["Cursor" : "A cursor handle", "Tuple" : "A tuple of parameter values matching the template for this cursor (structure)"], see_also : [cursor_next_execute / 3, cursor_all_execute / 2, cursor_N_execute / 4, session_sql_prepare / 4, session_sql_prepare_query / 5], summary : "Executes the parametrised prepared SQL statement represented by Cursor.", exceptions : [5 : "Cursor is not a valid cursor handle", 5 : "Type mismatch between parameter template specification for Cursor and actual tuple data", dbi_buffer_over : "Parameter value(s) too big for the buffer", dbi_error : "Error from DBMS while executing SQL associated with Cursor.", dbi_bad_template : "ParamTemplate not specified when Cursor was created", dbi_bad_cursor : "The Cursor is not in a state to execute a query (e.g. it was cancelled)"], eg : "\n  % note '?' in SQL in the syntax MySQL uses for placeholders. This may be\n  % different in other DBMS\n  transfer_(Session, Amount, FromAccount, ToAccount) :-\n      SQL = \"update accounts set balance = balance + ? \\\n                                               where id = ?\",\n      Deduct is - Amount,\n      % incbal(1.0,12) is the parameter template\n      session_sql_prepare(Session,incbal(1.0,12),SQL,Update),\n      cursor_next_execute(Update,incbal(Deduct,FromAccount)),\n      cursor_next_execute(Update,incbal(Amount,ToAccount)).", desc : html("<P>\n Executes the parameterised prepared SQL statement represented by Cursor,\n previously prepared by session_sql_prepare/4 or session_sql_prepare_query/5.\n The parameter values for this execution is supplied by Tuple.\n</P><P>\n Tuple is a structure whose name and arity match the parameter template\n when Cursor was prepared, and the arguments give the values for the\n parameters for this execution, and must be compatible with the type\n specified by the template, except that an argument can be an\n uninstantiated variable, to denote a NULL value for the corresponding\n parameter.\n</P><P>\n If the SQL statement is a query, and was prepared as a query using\n session_sql_prepare_query/5, results can be obtained from the query by\n the cursor_*_tuple family of predicates.\n</P><P>\n This predicate is called with default options for the cursor, i.e. it is\n equivalent to calling cursor_next_execute/3 with an empty Options list.\n \n")]).
:- comment(cursor_next_execute / 3, [amode : cursor_next_execute(++, +, ++), args : ["Cursor" : "A cursor handle", "Tuple" : "A tuple of parameter values matching the template for this cursor (structure)", "Options" : "Options (list of Option:Value pairs or nil)"], see_also : [cursor_next_execute / 2, cursor_all_execute / 2, cursor_N_execute / 4, session_sql_prepare / 4, session_sql_prepare_query / 5], summary : "Executes the parametrised prepared SQL statement represented by Cursor, with options Options.", exceptions : [5 : "Cursor is not a valid cursor handle", 5 : "Type mismatch between parameter template specification for Cursor and actual tuple data", 6 : "Invalid option specification in Options", dbi_buffer_over : "Parameter value(s) too big for the buffer", dbi_error : "Error from DBMS while executing SQL associated with Cursor.", dbi_bad_template : "ParamTemplate not specified when Cursor was created", dbi_bad_cursor : "The Cursor is not in a state to execute a query (e.g. it was cancelled)"], desc : html("<P>\n Executes the parameterised prepared SQL statement represented by Cursor,\n previously prepared by session_sql_prepare/4 or session_sql_prepare_query/5.\n The parameter values for this execution is supplied by Tuple. Options is \n a (possibly empty) list of <TT>Option:Value</TT> pairs, specifying \n DBMS-specific options for the cursor.\n</P><P>\n Tuple is a structure whose name and arity match the parameter template\n when Cursor was prepared, and the arguments give the values for the\n parameters for this execution, and must be compatible with the type\n specified by the template, except that an argument can be an\n uninstantiated variable, to denote a NULL value for the corresponding\n parameter.\n</P><P>\n If the SQL statement is a query, and was prepared as a query using\n session_sql_prepare_query/5, results can be obtained from the query by\n the cursor_*_tuple family of predicates.\n</P><P>\n MySQL specific:\n</P><P>\n Options is used to specify the type of cursor used. Currently this only\n applies to cursors for SQL queries. The options are:\n<DL>\n<P>\n<DT><STRONG><TT>buffering</TT></STRONG>\n<DD>Specifies where the result set of a SQL query is buffered. Value can be\n either <TT>client</TT> (the default) or <TT>server</TT>. By default, the\n whole of the result set for a query is copied to the client (i.e. the\n ECLiPSe process running lib(dbi)) after the SQL query is executed. The \n alternative is to leave the result set on the DBMS server, and only get\n the result tuples from the server one by one (with e.g. cursor_next_tuple/2).\n </P><P>\n The default buffering is on the client side, because this is the default\n of the MySQL C API, and in addition, it imposes certain restrictions on\n how the result tuples can be retrieved. However, as the whole result set\n is retreived, this can impose significant memory overheads if there are\n many tuples in the result set.\n </P><P>\n<DT><STRONG><TT>type</TT></STRONG>\n<DD>Specifies the type of cursor, and is only meaningful if the buffering\n option is set to server. Value can be either <TT>no_cursor</TT> (the\n default) or <TT>read_only</TT>.  These correspond to the MySQL statement \n attribute STMT_ATTR_CURSOR_TYPE of CURSOR_TYPE_NO_CURSOR and \n CURSOR_TYPE_READ_ONLY respectively (See the MySQL manual for details).\n</P><P>\n Only one active cursor of type no_cursor is allowed per session, and this\n active cursor must be closed before another query can be issued to the\n DBMS server. read_only cursor does not have this restriction, and several\n such cursors can be active at the same time. \n</DL>\n")]).
:- comment(cursor_next_tuple / 2, [amode : cursor_next_tuple(++, -), args : ["Cursor" : "A cursor handle", "ResultTuple" : "Returned result tuple"], summary : "Retrieve the next result tuple from the SQL query in ResultTuple", fail_if : "No more results are available for the SQL query", exceptions : [5 : "Cursor is not a valid cursor handle", 5 : "Unable to convert tuple result to ECLiPSe type", dbi_error : "Error from DBMS while executing SQL associated with Cursor.", dbi_error : "Error from DBMS while fetching result", dbi_not_query : "The SQL associated with Cursor is not a query and so cannot return results.", dbi_buffer_over : "Result value(s) too big for the buffer", dbi_cancelled : "The cursor have been cancelled."], eg : "\n  check_overdraft_limit(Session, Account) :-\n      L = [\"select count(id) from accounts \\\n          where     id = \",Account,\" and balance < overdraft\"],\n      concat_string(L,SQL),\n      session_sql_query(Session,c(0),SQL,OverdraftCheck),\n      cursor_next_tuple(OverdraftCheck,c(Count)),\n      Count = 0.\n", desc : html("<P>\n Retrieve the next result tuple from the SQL query represented by Cursor,\n and unify it with ResultTuple. Cursor is a cursor previously created with\n session_sql_query/4 or session_sql_prepare_query/5. ResultTuple is a\n structure with the same name and arity as defined by the tuple template\n when the cursor was created. The predicate converts the result to the type\n specified in the template, except that NULL values are returned as\n variables.\n</P><P>\n If the SQL query have not yet been executed, and it contains no\n parameters, then the SQL query will first be executed before retrieving\n the result.\n</P><P>\n cursor_next_tuple/2 will fail when all the results tuples for the query\n have been returned. If it is then called again for the same SQL query,\n this cancels the cursor, and raise the cursor cancelled error. \n")]).
:- comment(dbi_error_handler / 2, hidden).
:- comment(session_N_sql_prepare / 5, hidden).
:- comment(session_close / 1, [amode : session_close(++), args : ["Session" : "A session handle"], summary : "close a session for the DBMS", see_also : [session_start / 4, cursor_close / 1], exceptions : [5 : "Session is not a session handle"], desc : html("</P><P>\n This closes a session explicitly, disconnecting from the DBMS server. It \n takes  effect immediately. Any uncommitted transactional updates are rolled \n back. Some resources associated with the session are also freed.\n</P><P>\n Cursors for this session are not closed by the closing of the session,\n but they will no longer be able to access the database, but will still use\n some resources. It is recommended that cursors be closed before closing\n the session. All resources associated with a session are only freed when\n both the session and all the cursors are closed. \n")]).
:- comment(session_commit / 1, [amode : session_commit(++), args : ["Session" : "A session handle"], summary : "commits transactional changes made to the database.", see_also : [session_rollback / 1, session_transaction / 2], eg : "\n    session_sql(Session, \"insert into mytable values ('a')\", _),\n    session_commit(Session), % committing the insert of a to table mytable \n", exceptions : [5 : "Session is not a valid session handle", dbi_error : "Problems from DBMS during commit."], desc : html("<P>\n This commits any transactional updates to the database made within Session.\n By default, transactional updates are local to the session, and the changes\n only become global when a commit occurs, e.g. when session_commit/1 is\n executed. Note that non-transactional updates become globally visible \n immediately.\n</P><P>\n When executed within the scope of a session_transaction/2 goal, this\n predicate simply succeeds without doing anything.")]).
:- comment(session_error_value / 3, hidden).
:- comment(session_retrieve_N_tuples / 5, hidden).
:- comment(session_retrieve_lazy_tuples / 5, hidden).
:- comment(session_retrieve_tuple / 4, hidden).
:- comment(session_rollback / 1, [amode : session_rollback(++), args : ["Session" : "A session handle"], summary : "rollback transactional changes made to the database.", see_also : [session_commit / 1, session_transaction / 2], exceptions : [5 : "Session is not a valid session handle", dbi_error : "Problems from DBMS during rollback."], eg : "\n    session_sql(Session, \"insert into mytable values ('a')\", _),\n    session_commit(Session), % committing the insert of a to table mytable \n    session_sql(Session, \"insert into mytable values ('b')\", _),\n    session_sql(Session, \"insert into mytable values ('c')\", _),\n    % undo the inserting of b and c into table mytable (if tranactional)\n    session_rollback(Session), \n", desc : html("<P>\n  This undoes all <STRONG>transactional</STRONG> changes made to the database since the last\n  commit for this session. Note some DBMS can operate non-transactionally\n  (for example, non-transactional tables in MySQL), such operations cannot\n  be undone. Also, even for transactions, not all changes can be undone.\n  See DBMS's manual for details.\n</P><P>\n  Outside a transaction this predicate will either succeed if rollback was\n  successful, or raise an error otherwise.\n<P></P>\n  When executed within the scope of a session_transaction/2 goal, this \n  predicate will simply abort the complete outer transaction.\n</P><P>\n NOTE: This predicate behaves very differently, depending on\n whether it is in a transaction or not. It is always advisable not\n to rely on it succeeding, but rather to fail or abort immediately\n after it adopting a coding style that causes a Prolog failure\n whenever a transaction cannot be completed.")]).
:- comment(session_sql / 3, [amode : session_sql(++, ++, -), args : ["Session" : "A session handle", "SQL" : "A SQL statement (string)", "RowProcessedCount" : "Number of rows affected by this statement"], summary : "Executes a SQL statement on the database server.", exceptions : [5 : "Session is not a valid session handle or SQL not a string", dbi_error : "database returned an error when executing SQL."], eg : "\nmake_accounts(Session) :-\n    session_sql(Session,\n        \"create table accounts \\\n         (id           decimal(4)      not null,\\\n          balance      decimal(9,2)    default 0.0 not null, \\\n          overdraft    decimal(9,2)    default 0.0 not null \\\n         )\" ,_),\n    session_sql(Session,\n        \"insert into accounts (id,balance) values (1001,1200.0)\",1),\n    session_sql(Session,\n        \"insert into accounts (id,balance) values (1002,4300.0)\",1).\n", desc : html("<P>\n Executes a SQL statement (without parameters) on the database server. The\n SQL statement should not have a terminating semicolon (;). If supported by\n the DBMS, SQL can consist of multiple statements separated by semicolons.\n RowProcessedCount will be unified with the number of rows affected by this\n statement.\n</P><P>\n In the case of data definition language statements the RowProcessedCount\n parameter is undefined and should be passed as a free variable.\n</P><P>\n In the case of data manipulation language statements, the\n RowProcessedCount is the number of rows, deleted, modified or inserted\n by the SQL statement.\n</P><P>\n The SQL statement must be valid for the DBMS to execute. It can contain\n NULL characters, i.e. it can contain binary data.\n</P><P>\n Any results generated by the statement is discarded.")]).
:- comment(session_sql_prepare / 4, [amode : session_sql_prepare(++, +, ++, -), args : ["Session" : "A session handle", "ParamTemplate" : "Template defining the types of the parameters (structure or [])", "SQL" : "A SQL statement in prepared syntax (string)", "Cursor" : "Returned cursor handle"], summary : "Prepares a SQL statement for execution by the DBMS.", see_also : [cursor_next_execute / 2, cursor_all_execute / 2, cursor_N_execute / 4, cursor_close / 1, session_sql / 3, session_sql_prepare_query / 5], exceptions : [5 : "Session is not a valid session handle, or SQL not a string, or ParamTemplate not a structure", dbi_error : "Error from DBMS while preparing SQL", dbi_bad_template : "ParamTemplate has the wrong arity"], eg : "\n  % note '?' in SQL in the syntax MySQL uses for placeholders. This may be\n  % different in other DBMS\n  transfer_(Session, Amount, FromAccount, ToAccount) :-\n      SQL = \"update accounts set balance = balance + ? \\\n                                               where id = ?\",\n      Deduct is - Amount,\n      % incbal(1.0,12) is the parameter template\n      session_sql_prepare(Session,incbal(1.0,12),SQL,1,Update),\n      cursor_next_execute(Update,incbal(Deduct,FromAccount)),\n      cursor_next_execute(Update,incbal(Amount,ToAccount)).", desc : html("<P>\n Prepares a SQL statement for execution. The statement is not actually\n executed, and a cursor_*_execute family of predicate is required to\n execute the predicate. This facility is only available if the DBMS\n supports prepared statements, and the SQL statement has to be written in\n the prepared statement syntax of the DBMS. The predicate returns the\n cursor handle representing this prepared statement in Cursor, which can\n then be used in subsequent library predicates.\n</P><P>\n A prepared SQL statement is parsed by the DBMS, so that it could be\n executed more efficiently. It can also be parameterised, where the \n parameters represent values to be filled in when the statement is\n executed. The statement can be executed multiple times with different\n parameters. The types of the parameters is specified by ParamTemplate,\n which is a Prolog structure of arity M (where M is the number of\n parameters for the statement), or the nil atom [] if there are no parameters.\n See the general description of this library or the manual for a\n description of the template specification.\n </P><P>\n The SQL statement must be valid for the DBMS to execute. It can contain\n NULL characters, i.e. it can contain binary data. The SQL statement cannot\n return any results. If it does, then an error would be raised when the SQL\n statement is actually executed. \n</P><P>\n Note that some DBMS restricts which SQL statements can be prepared. If an\n SQL statement cannot be prepared, it can still be executed using\n session_sql/3. ")]).
:- comment(session_sql_prepare_query / 5, [amode : session_sql_prepare_query(++, +, +, ++, -), args : ["Session" : "A session handle", "ParamTemplate" : "Template defining the types of the parameters (structure or [])", "ResultTemplate" : "Template defining the types of results tuple (structure)", "SQLQuery" : "A SQL query in prepared syntax (string)", "Cursor" : "Returned cursor handle"], summary : "Prepares a SQL query for execution by the DBMS.", exceptions : [5 : "Session is not a valid session handle, or SQLQuery not a string, or ResultTemplate or ParamTemplate not a structure", dbi_error : "Error from DBMS while preparing SQLQuery.", dbi_bad_template : "ResultTemplate or ParamTemplate has the wrong arity"], see_also : [cursor_next_execute / 2, cursor_next_tuple / 2, cursor_all_tuples / 2, cursor_N_tuples / 4, session_sql / 3, session_sql_query / 4], eg : "\n make_check_overdraft_limit(Session, Cursor) :-\n      % note '?' in SQL in the syntax MySQL uses for placeholders. This may be\n      % different in other DBMS\n      SQL = \"select count(id) from accounts where ID = ? \\\n                 and balance < overdraft\",\n      session_sql_prepare_query(Session,a(0),c(0),SQL,1,Cursor).", desc : html("<P>\n Prepares a SQL query for execution. The query is not actually\n executed, and a cursor_next_execute/2 needs to be called to execute the \n SQL query, giving values to any parameters. Then the cursor_*_tuple family\n of predicates can be used to obtain the results. This facility is only\n available if the DBMS supports prepared statements, and the SQL query has\n to be written in the prepared statement syntax of the DBMS. The predicate\n returns the cursor handle representing this prepared query in Cursor,\n which can then be used in subsequent library predicates.\n</P><P>\n A prepared SQL statement is parsed by the DBMS, so that it could be\n executed more efficiently. It can also be parameterised, where the \n parameters represent values to be filled in when the statement is\n executed. The statement can be executed multiple times with different\n parameters. The types of the parameters is specified by ParamTemplate,\n which is a Prolog structure of arity M (where M is the number of\n parameters for the statement), or the nil atom [] if there are no parameters.\n See the general description of this library or the manual for a\n description of the template specification.\n</P><P>\n The SQL query returns result in tuples of N elements each. Each tuple is\n mapped to a Prolog structure of arity N. ResultTemplate is a structure of\n arity N specifying the types of the return data for ECLiPSe. See the\n general description of this library or the manual for a description of \n the template specification.\n</P><P>\n The SQL query must be valid for the DBMS to execute. It can contain\n NULL characters, i.e. it can contain binary data.\n</P><P>\n Note that some DBMS restricts which SQL statements can be prepared. If an\n SQL statement cannot be prepared, it can still be executed using\n session_sql/3.\n</P><P>\n MySQL specific note: not all SQL statements can be prepared by MySQL.\n Refer to the MySQL manual for details.\n")]).
:- comment(session_sql_prepare_query_N / 6, hidden).
:- comment(session_sql_query / 4, [amode : session_sql_query(++, +, ++, -), args : ["Session" : "A session handle", "ResultTemplate" : "Template defining the types of results tuple (structure)", "SQLQuery" : "A SQL statement query (string)", "Cursor" : "Returned cursor handle"], summary : "Executes a SQL query on the database server.", see_also : [session_sql_query / 5, cursor_next_tuple / 2, cursor_all_tuples / 2, cursor_N_tuples / 4, session_sql_prepare_query / 5, cursor_close / 1], eg : "\n  check_overdraft_limit(Session, Account) :-\n      L = [\"select count(id) from accounts \\\n          where     id = \",Account,\" and balance < overdraft\"],\n      concat_string(L,SQL),\n      session_sql_query(Session,c(0),SQL,OverdraftCheck),\n      cursor_next_tuple(OverdraftCheck,c(Count)),\n      Count = 0.\n", exceptions : [5 : "Session is not a valid session handle, or SQLQuery not a string, or ResultTemplate not a structure", dbi_error : "Error from DBMS while executing SQLQuery.", dbi_bad_template : "ResultTemplate has the wrong arity"], desc : html("<P>\n Executes a SQL query on the database server. The predicate returns in\n Cursor the cursor handle for this SQL query, and the results can then be\n retrieved using cursor_*_tuple family of predicates. \n</P><P>\n The SQL query returns result in tuples of N elements each. Each tuple is\n mapped to a Prolog structure of arity N. ResultTemplate is a structure of\n arity N specifying the types of the return data for ECLiPSe. See the\n general description of this library or the manual for a description of \n the template specification.\n </P><P>\n The SQL query must be valid for the DBMS to execute. It can contain\n NULL characters, i.e. it can contain binary data.\n </P><P>\n This predicate is called with default options for the cursor, i.e. it is\n equivalent to calling session_sql_query/5 with an empty Options list.\n")]).
:- comment(session_sql_query / 5, [amode : session_sql_query(++, +, ++, ++, -), args : ["Session" : "A session handle", "ResultTemplate" : "Template defining the types of results tuple (structure)", "SQLQuery" : "A SQL statement query (string)", "Options" : "Options (list of Option:Value pairs or nil)", "Cursor" : "Returned cursor handle"], summary : "Executes a SQL query on the database server with options specified by Options.", see_also : [session_sql_query / 4, cursor_next_tuple / 2, cursor_all_tuples / 2, cursor_N_tuples / 4, session_sql_prepare_query / 5, cursor_close / 1], eg : "\n  check_overdraft_limit(Session, Account) :-\n      L = [\"select count(id) from accounts \\\n          where     id = \",Account,\" and balance < overdraft\"],\n      concat_string(L,SQL),\n      % the buffering:server option is MySQL specific\n      session_sql_query(Session,c(0),SQL,[buffering:server],OverdraftCheck),\n      cursor_next_tuple(OverdraftCheck,c(Count)),\n      Count = 0.\n", exceptions : [5 : "Session is not a valid session handle, or SQLQuery not a string, or ResultTemplate not a structure", 6 : "Invalid option specification in Options", dbi_error : "Error from DBMS while executing SQLQuery.", dbi_bad_template : "ResultTemplate has the wrong arity"], desc : html("<P>\n Executes a SQL query on the database server. The predicate returns in\n Cursor the cursor handle for this SQL query, and the results can then be\n retrieved using cursor_*_tuple family of predicates. Options is \n a (possibly empty) list of <TT>Option:Value</TT> pairs, specifying \n DBMS-specific options for the cursor.\n</P><P>\n The SQL query returns result in tuples of N elements each. Each tuple is\n mapped to a Prolog structure of arity N. ResultTemplate is a structure of\n arity N specifying the types of the return data for ECLiPSe. See the\n general description of this library or the manual for a description of \n the template specification.\n </P><P>\n The SQL query must be valid for the DBMS to execute. It can contain\n NULL characters, i.e. it can contain binary data.\n</P><P>\n MySQL specific:\n</P><P>\n Options is used to specify the type of cursor used. Currently this only\n applies to cursors for SQL queries. The options are:\n<DL>\n<P>\n<DT><STRONG><TT>buffering</TT></STRONG>\n<DD>Specifies where the result set of a SQL query is buffered. Value can be\n either <TT>client</TT> (the default) or <TT>server</TT>. By default, the\n whole of the result set for a query is copied to the client (i.e. the\n ECLiPSe process running lib(dbi)) after the SQL query is executed. The \n alternative is to leave the result set on the DBMS server, and only get\n the result tuples from the server one by one (with e.g. cursor_next_tuple/2).\n </P><P>\n The default buffering is on the client side, because this is the default\n of the MySQL C API, and in addition, it imposes certain restrictions on\n how the result tuples can be retrieved. However, as the whole result set\n is retreived, this can impose significant memory overheads if there are\n many tuples in the result set. On the other hand, there is no restrictions\n on how many active client buffered cursor is allowed per session at the\n same time, but only one active server buffered cursor is allowed at any\n one time -- a query result must be exhausted or the cursor explicitly\n closed before another query can be issued for that session.\n </P><P>\n<DT><STRONG><TT>type</TT></STRONG>\n<DD>This option is not relevant for the direct SQL queries of\n session_sql_query/4. It is only relevant for prepared queries, and has no\n effect here.\n</DL>\n")]).
:- comment(session_sql_query_N / 6, hidden).
:- comment(session_start / 3, hidden).
:- comment(session_start / 4, [amode : session_start(++, ++, ++, -), args : ["Login" : "DBMS login: ID or ID@Host. (ID,Host: string or atom)", "Password" : "Password (string or atom)", "Options" : "Options (list or nil)", "Session" : "Session handle"], summary : "starts a new session for the DBMS", see_also : [session_close / 1], eg : "\n     % connecting to a database server running on the local machine, as \n     % database user sqluser with password password\n     session_start(\"sqluser\", \"password\", [], S).\n\n     % connecting to a database server running on machine my.sql.host,\n     % as database user sqluser with password password, and selecting\n     % to use the database mydb\n     session_start(sqluser@my.sql.host, password, [dbame:\"mydb\"], S).\n", desc : html("<P>\n Starts a new session with the DBMS. A new connection with the DBMS server is\n established, with the supplied DBMS user ID. Optionally, a host could be\n supplied, if the server is located on a remote machine. The host could be\n either the machine name, or an IP address. A session handle for this new\n connection is returned by the predicate.\n</P><P>\n The session is closed automatically if the program backtracks or aborts back\n beyond this call. \n</P><P>\n Options is a (possibly empty) list of <TT>Option:Value</TT> pairs, specifying any\n options for this session. The following options are supported:\n<DL>\n<P>\n<DT><STRONG><TT>dbname</TT></STRONG>\n <DD>Specifies the name of the database to use. Value can be a\n     string or an atom. The same effect can also be achieved by issuing the\n     SQL statement <TT>USE ...</TT> (where ... is the dbname).\n<P>\n<DT><STRONG><TT>storage</TT></STRONG>\n <DD>(MySQL specific) specifies the default storage model to use for\n     the tables. Value can be either the string/atom <TT>transactional</TT>\n     or <TT>non-transactional</TT>. For transactional tables, changes are\n     local until they are committed. Uncommitted changes can be rollback.\n     For non-transactional tables, changes are global. InnoDB (with autocommit\n     turned off) is the storage  engine used for transactional, and MyISAM is\n     the storage engine used for non-transactional. See the MySQL manual for\n     details on storage engines and their effect on tables.\n</DL>"), exceptions : [5 : "Login, Password or Options not of the correct type.", 6 : "Invalid option specification in Options.", dbi_bad_field : "Problems with Option's argument.", dbi_error : "Problems connecting to DBMS server."]]).
:- comment(session_transaction / 2, [amode : session_transaction(++, +), args : ["Session" : "A session handle", "Goal" : "Prolog goal that implements a database update."], summary : "executes Goal as a database transaction.", see_also : [session_rollback / 1, session_commit / 1], exceptions : [5 : "Session is not a valid session handle", abort : "session_rollback/1 called within Goal"], eg : "\ntransfer(Session, Amount,FromAccount,ToAccount) :-\n     session_transaction(Session, \n         transfer_(Session,Amount,FromAccount,ToAccount)\n     ).\n\n% note '?' in SQL in the syntax MySQL uses for placeholders. This may be\n% different in other DBMS\ntransfer_(Session, Amount, FromAccount, ToAccount) :-\n    SQL = \"update accounts set balance = balance + ? \\\n                                             where id = ?\",\n    Deduct is - Amount,\n    session_sql_prepare(Session,incbal(1.0,12),SQL,1,Update),\n    cursor_next_execute(Update,incbal(Deduct,FromAccount)),\n    cursor_next_execute(Update,incbal(Amount,ToAccount)).\n", desc : html("<P>\n This executes Goal as a database transaction. This predicate is only useful\n if the database supports transactions (i.e. changes are local until committed).\n If Goal succeeds session_transaction/2  commits the update, cuts any\n alternative solutions to Goal and succeeds itself.\n</P><P>\n If Goal fails or causes a database error, session_transaction/2 fails and\n rolls back any changes done to the database.\n</P><P>\n If Goal aborts, the update is rolled back, and session_transaction/2 aborts.\n</P><P>\n MySQL specific note: transactions are supported for transactional tables only\n (i.e. stored with a storage engine with transaction-safe capabilities).\n</P><P?\n NOTE: It is a programming error to have some previous uncommitted\n database updates at the time of calling session_transaction/2. \n</P><P>\n Calls of this predicate can be nested, but only the outermost transaction \n is real. All the inner transactions are simply equivalent to call(Goal).\n This way it is possible to write a call to session_transaction/2, into \n some code that implements a simple update, but then to include that simple \n update into a larger transaction.\n</P><P>\n Similarly session_commit/1 and session_rollback/1 alter their behaviour so\n that they can be used within a transaction.\n</P><P>\n Transactions are local to one session so there is no way to safely make an \n update relating to several sessions.")]).
