From 25ce50da64b0b3ed4a6bb2b63fa8aa66edad764a Mon Sep 17 00:00:00 2001 From: Francesco Chemolli <li@kinkie.it> Date: Tue, 14 Oct 1997 23:55:43 +0200 Subject: [PATCH] Msql mod 1.1 pre 6. Rev: src/modules/Msql/COPYING:1.1.1.1 Rev: src/modules/Msql/ChangeLog:1.1.1.1 Rev: src/modules/Msql/INSTALL:1.1.1.1 Rev: src/modules/Msql/Makefile.in:1.1.1.1 Rev: src/modules/Msql/NOTES:1.1.1.1 Rev: src/modules/Msql/configure.in:1.1.1.1 Rev: src/modules/Msql/doc/msql:1.1.1.1 Rev: src/modules/Msql/msql_config.h.in:1.1.1.1 Rev: src/modules/Msql/msqlmod.c:1.1.1.1 Rev: src/modules/Msql/test_msqlmod.pike:1.1.1.1 Rev: src/modules/Msql/testsuite.in:1.1.1.1 --- .gitattributes | 3 + src/modules/Msql/COPYING | 17 + src/modules/Msql/ChangeLog | 49 ++ src/modules/Msql/INSTALL | 17 + src/modules/Msql/Makefile.in | 26 + src/modules/Msql/NOTES | 2 + src/modules/Msql/configure.in | 108 ++++ src/modules/Msql/doc/msql | 371 ++++++++++++++ src/modules/Msql/msql_config.h.in | 23 + src/modules/Msql/msqlmod.c | 783 +++++++++++++++++++++++++++++ src/modules/Msql/test_msqlmod.pike | 33 ++ src/modules/Msql/testsuite.in | 0 12 files changed, 1432 insertions(+) create mode 100644 src/modules/Msql/COPYING create mode 100644 src/modules/Msql/ChangeLog create mode 100644 src/modules/Msql/INSTALL create mode 100644 src/modules/Msql/Makefile.in create mode 100644 src/modules/Msql/NOTES create mode 100644 src/modules/Msql/configure.in create mode 100644 src/modules/Msql/doc/msql create mode 100644 src/modules/Msql/msql_config.h.in create mode 100644 src/modules/Msql/msqlmod.c create mode 100755 src/modules/Msql/test_msqlmod.pike create mode 100644 src/modules/Msql/testsuite.in diff --git a/.gitattributes b/.gitattributes index c24f286a8b..e293d87cd9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -92,6 +92,9 @@ testfont binary /src/modules/Image/x.c foreign_ident /src/modules/MIME/mime.c foreign_ident /src/modules/MIME/module.pmod.in foreign_ident +/src/modules/Msql/configure.in foreign_ident +/src/modules/Msql/msql_config.h.in foreign_ident +/src/modules/Msql/msqlmod.c foreign_ident /src/modules/Mysql/Makefile.in foreign_ident /src/modules/Mysql/acconfig.h foreign_ident /src/modules/Mysql/configure.in foreign_ident diff --git a/src/modules/Msql/COPYING b/src/modules/Msql/COPYING new file mode 100644 index 0000000000..ae18060d52 --- /dev/null +++ b/src/modules/Msql/COPYING @@ -0,0 +1,17 @@ +This code is (c) 1996,1997 Francesco Chemolli <kinkie@comedia.it> + +It may be copied and distributed freely under the terms of the GNU +General Public License, version 2. +You should have received it along with your pike sources. + +Use this software at your own risk. There's NO WARRANTY, EITHER EXPLICIT +OR IMPLICIT, that this software won't disrupt your work or blow your +computer. If this can console you, I'm using it, and my computer is still +intact :). + +If you use this software and find it useful, please take the time to +send me an email. This will definitely help me keep my overinflated ego +above guard level, and encourage me improve this software. + + Hoping to be useful, + Francesco Chemolli diff --git a/src/modules/Msql/ChangeLog b/src/modules/Msql/ChangeLog new file mode 100644 index 0000000000..ee57e1a2d0 --- /dev/null +++ b/src/modules/Msql/ChangeLog @@ -0,0 +1,49 @@ +Mon Jun 30 13:57:46 1997 Francesco Chemolli <kinkie@ai-chan> + + * configure.in, configure: + Configure fixes which allow much more flexibility in configuration and + maintainance. + +Sun Jun 29 20:05:50 1997 Francesco Chemolli <kinkie@ai-chan> + + * msqlmod.c: + Corretto un errore nel parsing degli argomenti in list_index() + + * configure.in: Eliminati dei doppi test in configure.in + +Thu Jun 19 10:26:29 1997 Francesco Chemolli <kinkie@ai-chan> + + * msqlmod.c: + I had unavvertitedly rolled back to an older version of msqlmod.c. + Corrected the reinserted bugs. + +Fri Jun 13 21:42:48 1997 Francesco Chemolli <kinkie@ai-chan> + + * ChangeLog, INSTALL: Some documentation. + + * Makefile.in, msqlmod.c: + Seems to work now. I guess it's time to bump the version number up... + + * msqlmod.c, Makefile.in: Piccola correzione in msqlmod.c + Sempre alla caccia di un hook in dynamic_module_makefile + + * Makefile.in: + Sulla strada per sistemare la compilazione... dovremmo quasi esserci. + + * lib/msql.h, lib/msql.pike, configure.in, msql_config.h.in, msqlmod.c, Makefile.in, configure: + Compattata la distribuzione: ora installa le librerie + partendo dalla sola directory pike/src/modules/Msql. + + * configure, dependencies: Aggiunto il file vuoto "dependencies". + +Wed Apr 2 11:31:44 1997 Francesco Chemolli <kinkie@ai-chan> + + * COPYING, ChangeLog, INSTALL, Makefile.in, NOTES, configure, configure.in, doc/msql, msqlmod.c, test_msqlmod.pike, testsuite.in: + Initial revision + + * COPYING, ChangeLog, INSTALL, Makefile.in, NOTES, configure, configure.in, doc/msql, msqlmod.c, test_msqlmod.pike, testsuite.in: + Initial version of the Msql module for Pike/0.5b4. + The Makefile has to be adjusted to auto-install all necessary things, + but this needs investigation in the Pike makefiles structure. + Maybe the documentation is out of date. + diff --git a/src/modules/Msql/INSTALL b/src/modules/Msql/INSTALL new file mode 100644 index 0000000000..fdc5058922 --- /dev/null +++ b/src/modules/Msql/INSTALL @@ -0,0 +1,17 @@ +To install this module you need to untar your pike sources, +and in the src/modules/ directory untar this code. + +Then in the src/ directory, +./configure +make + +You need to have the header file msql.h and the library libmsql.a in one of the +'standard' locations. If you have it, and the configure-script +doesn't find it, you can set BEFORE RUNNING CONFIGURE the environment +variables CPPFLAGS and LDFLAGS to the flags you'd use for your compiler +to locate the include and library files msql.h and libmsql.a + +Documentation is availible in the doc/ directory, it will be HTML-ized +if you +make html_docs +as Msql_msql.html diff --git a/src/modules/Msql/Makefile.in b/src/modules/Msql/Makefile.in new file mode 100644 index 0000000000..8f110bddd9 --- /dev/null +++ b/src/modules/Msql/Makefile.in @@ -0,0 +1,26 @@ +SRCDIR=@srcdir@ +VPATH=@srcdir@:@srcdir@/../..:../.. +OBJS=msqlmod.o +MODULE_LDFLAGS=@LDFLAGS@ @LIBS@ +MODULE_CPPFLAGS=@CPPFLAGS@ +MSQL_SUPPORTED=@MSQL_SUPPORTED@ + +all: module_install + +module_install: include_install glue_install mod_install + +include_install: lib/msql.h + if [ x$(MSQL_SUPPORTED) = xyes ] ; then cp lib/msql.h $(TMP_LIBDIR)/include/msql.h; fi + +glue_install: lib/msql.pike + if [ x$(MSQL_SUPPORTED) = xyes ] ; then cp lib/msql.pike $(TMP_LIBDIR)/modules/Sql.pmod/msql.pike; fi + +mod_install: + if [ x$(MSQL_SUPPORTED) = xyes ] ; then make dummy; fi + +@dynamic_module_makefile@ + +spotless: clean + -rm config.* + +@dependencies@ diff --git a/src/modules/Msql/NOTES b/src/modules/Msql/NOTES new file mode 100644 index 0000000000..2f9a26d9e4 --- /dev/null +++ b/src/modules/Msql/NOTES @@ -0,0 +1,2 @@ +Although it seems that mSQL/2.0 has some support for server statistics, +it's really VERY VERY primitive, so I won't add it for now. diff --git a/src/modules/Msql/configure.in b/src/modules/Msql/configure.in new file mode 100644 index 0000000000..0bd2e56df1 --- /dev/null +++ b/src/modules/Msql/configure.in @@ -0,0 +1,108 @@ +dnl $Id: configure.in,v 1.1.1.1 1997/10/14 21:55:42 grubba Exp $ +dnl (C) 1997 Francesco Chemolli <kinkie@comedia.it> + +AC_INIT(msqlmod.c) +AC_CONFIG_HEADER(msql_config.h) + +AC_ARG_WITH(msql, [ --with(out)-msql mSQL Database engine support ],[] +,[with_msql=yes]) + +echo "Configuring msql module, (C) 1996,1997 Francesco Chemolli <kinkie@comedia.it>" + +sinclude(../module_configure.in) + +dnl set up "reasonable" search paths +pike_msql_reasonable_prefixes="/usr/local /usr /opt" +pike_msql_reasonable_directories="Minerva Hughes minerva hughes msql Msql msql2 Msql2 /" +pike_msql_extra_include_directories="$HOME/include" +pike_msql_extra_lib_directories="$HOME/lib" + +for a in $pike_msql_reasonable_prefixes +do + for b in $pike_msql_reasonable_directories + do + pike_msql_reasonable_include_searchpath="$pike_msql_reasonable_include_searchpath $a/$b/include" + pike_msql_reasonable_lib_searchpath="$pike_msql_reasonable_lib_searchpath $a/$b/lib" + done +done +pike_msql_reasonable_include_searchpath="$pike_msql_reasonable_include_searchpath $pike_msql_extra_include_directories" +pike_msql_reasonable_lib_searchpath="$pike_msql_reasonable_lib_searchpath $pike_msql_extra_lib_directories" + +dnl let's start the actual tests +if test x$with_msql = xyes; then + OLD_CPPFLAGS=$CPPFLAGS + OLD_LIBS=$LIBS + OLD_LDFLAGS=$LDFLAGS + + AC_MSG_CHECKING(for location of mSQL include files) + AC_CACHE_VAL(pike_cv_msql_include_dir, [ + for pike_cv_msql_include_dir in $pike_msql_reasonable_include_searchpath no + do + if test -f $pike_cv_msql_include_dir/msql.h; then + break + fi + done ]) + if test x$pike_cv_msql_include_dir != xno; then + AC_MSG_RESULT(found.) + else + AC_MSG_RESULT(not found.) + fi +dnl AC_MSG_RESULT($pike_cv_msql_include_dir) + + if test x$pike_cv_msql_include_dir != xno; then + CPPFLAGS="$CPPFLAGS -I$pike_cv_msql_include_dir" + fi + + AC_CHECK_HEADERS(msql.h) + + if test "$ac_cv_header_msql_h" != "no"; then + AC_MSG_CHECKING(for location of mSQL library) + AC_CACHE_VAL(pike_cv_msql_lib_dir, [ + for pike_cv_msql_lib_dir in $pike_msql_reasonable_lib_searchpath no + do + if test -f $pike_cv_msql_lib_dir/libmsql.a; then + break + fi + done + ] ) + if test x$pike_cv_msql_lib_dir != xno; then + AC_MSG_RESULT(found.) + else + AC_MSG_RESULT(not found.) + fi +dnl AC_MSG_RESULT($pike_cv_msql_lib_dir) + fi + + if test "$pike_cv_msql_lib_dir" != "no" ; then + LDFLAGS="$LDFLAGS -L$pike_cv_msql_lib_dir" + # On some OS's (Solaris 2) these are requiered + AC_CHECK_LIB(socket, socket, [], []) + AC_CHECK_LIB(nsl, gethostbyname, [], []) + + AC_CHECK_LIB(msql, msqlClose) + if test "$ac_cv_lib_msql_msqlClose" != "no"; then + pike_cv_found_msql="yes" + MSQL_SUPPORTED="yes" + fi + fi + + if test x$pike_cv_found_msql = xyes; then + AC_CHECK_FUNCS(msqlListIndex) + if test "$ac_cv_func_msqlListIndex" = "yes"; then + echo "We're supporting mSQL version 2.0, good..." + else + echo "We're supporting mSQL version 1.0" + fi + fi +fi + +if test "x$pike_cv_found_msql" != "xyes"; then + CPPFLAGS=$OLD_CPPFLAGS + LDFLAGS=$OLD_LDFLAGS + LIBS=$OLD_LIBS +fi + +AC_SUBST(LIBS) +AC_SUBST(MSQL_SUPPORTED) + +AC_OUTPUT(./Makefile) diff --git a/src/modules/Msql/doc/msql b/src/modules/Msql/doc/msql new file mode 100644 index 0000000000..bb7d9182bf --- /dev/null +++ b/src/modules/Msql/doc/msql @@ -0,0 +1,371 @@ +NAME + /precompiled/sql/msql - mSQL SQL server interface + +AUTHOR + Francesco Chemolli <kinkie@comedia.it> + +DESCRIPTION + This is an interface to the mSQL database server. + This module may or may not be availible on your Pike, depending + whether the appropriate include and library files (msql.h and libmsql.a + respectively) could be found at compile-time. Note that you DO NOT + need to have a mSQL server running on your host to use this module: + you can connect to the database over a TCP/IP socket + + Please notice that unless you wish to specifically connect to a mSQL + server, you'd better use the /precompiled/sql program instead. + I and Henrik Grubbstrom have (hopefully) designed a server-independent + sql-server-class. The interfaces to all existing sql-classes + are consistant. Using /precompiled/sql ensures that your pike + applications will run with any supported SQL server without changing + a single line of code. + + Also notice that we are currently undergoing a major release change + for mSQL. Some functions may mSQL/2.0 -specific, and thus missing on + hosts runnign mSQL/1.0.* + +SEE ALSO + /precompiled/sql/mysql, /precompiled/sql + +============================================================================ +NAME + create - initialize module and/or select database + +SYNTAX + #include <msql.h> + void msql->create(); + or + void msql->create (string dbserver); + or + void msql->create (string dbserver, string dbname); + +DESCRIPTION + With one argument, this function + tries to connect to the specified (use hostname or IP address) database + server. To connect to a server running on the local host via UNIX domain + sockets use "localhost". To connect to the local host via TCP/IP sockets + you have to use the IP address "127.0.0.1". + With two arguments it also selects a database to use on the server. + With no arguments it tries to connect to the server on localhost, using + UNIX sockets. + +NOTA BENE + You need to have a database selected before using it, otherwise you'll + get exceptions when you try to query it. + Also notice that this function CAN raise exceptions if the db + server doesn't respond, if the database doesn't exist or is not accessible + by you. + You don't need bothering about syncronizing the connection to the database: + it is automatically closed (and the database is sync-ed) when the msql + object is destroyed. + +SEE ALSO + msql->select_db + +============================================================================ +NAME + select_db - Select the database you're using + +SYNTAX + #include <msql.h> + void msql->select_db(string dbname); + +DESCRIPTION + Before querying a database you have to select it. This can be accomplished + in two ways: the first is calling the create() function with two arguments, + another is calling it with only one argument and then calling select_db(). + You can also use this function to change the database you're querying, + as long as it is on the same server you connected earlier. + +NOTA BENE + This function CAN raise exceptions in case something goes wrong + (for example: unexistant database, insufficient permissions, whatever). + +SEE ALSO + msql->create + +============================================================================ +NAME + query - query the db server + +SYNTAX + #include <msql.h> + array(mapping(string:mixed)) query (string sql_query); + +DESCRIPTION + This is all you need to query the database. It takes as argument an SQL + query string (i.e.: "SELECT foo,bar FROM baz WHERE name like '%kinkie%'" + or "INSERT INTO baz VALUES ('kinkie','made','this')") + and returns a data structure containing the returned values. + The structure is an array (one entry for each returned row) of mappings + which have the column name as index and the column contents as data. + So to access a result from the first example you would have to do + "results[0]->foo" + A query which returns no data results in an empty array (and NOT in a 0). + Also notice that when there is a column name clash (that is: when you + join two or more tables which have columns with the same name), the + clashing columns are renamed to <tablename>+"."+<column name>. + To access those you'll have to use the indexing operator '[] + (i.e.: results[0]["foo.bar"]). + Errors (both from the interface and the SQL server) are reported via + exceptions, you may definitely want to catch them. + Also note that if the query is NOT a of SELECT type, but UPDATE or + MODIFY, the returned value is an empty array. + THIS IS NOT AN ERROR. Errors are reported ONLY via exceptions. + Error messages are not particularly verbose, since they account + only for errors inside the driver. To get server-related error messages, + you have to use the msql->error() function. + +SEE ALSO + msql->error + +============================================================================ +NAME + list_dbs - list all databases availible on the server + +SYNTAX + #include <msql.h> + array(string) list_dbs(); + or + array(string) list_dbs(string wildcard); + +DESCRIPTION + Returns an array containing the names of all databases availible on + the system. Will throw an exception if there is no server connected. + If an argument is specified, it will return only those databases + whose name matches the given wildcard (which must be a glob). + +============================================================================ +NAME + list_tables - list all the tables in the current database + +SYNTAX + #include <msql.h> + array(string) list_tables(); + or + array(string) list_tables(string wildcard); + +DESCRIPTION + Returns an array containing the names of all the tables in the currently + selected database. Will throw an exception if we aren't connected to + a database. + If an argument is specified, it will return only those tables + whose name matches the given wildcard (which must be a glob). + +============================================================================ +NAME + list_fields - describe the fields of a table in the database. + +SYNTAX + mapping(string:mapping(string:mixed)) list_fields(string table); + +DESCRIPTION + Returns a mapping describing the fields of a table in the database. + The returned value is a mapping, indexed on the column name, + of mappings. + These contain curretly the fields: + + "type"(string) describes the field's mSQL data type ("char","integer",...) + + "length"(int) it describes the field's length. It is only interesting for + char fields, in fact. Notice that mSQL/2.0's "text" fields + make this type obsolete, and this information useless. Also + notice that the strings returned by msql->query() have the correct length. + This field only specifies the _maximum_ length a "char()" field can have. + + "table"(string) The table this field is in. Added only for + interface compliancy. + + "flags"(multiset(string)) it's a multiset containing textual + descriptions of the server's flags associated with the current field. + Currently it can be empty, or contain "unique" or "not null". + + + +WARNING + As of version 1.0 of the mSQL module and 1.4 of the mysql module, this + function is _NOT_ generic-sql-interface compliant (it misses the + second argument, and this will break the interface). + +SEE ALSO + msql->query + +============================================================================ +NAME + error - tell what the last server error was. + +SYNTAX + string error(); + +DESCRIPTION + This function returns the textual description of the last server-related + error. Returns 0 if no error has occurred yet. + +SEE ALSO + msql->query + +============================================================================ +NAME + server_info - describe what server are we talking to. + +SYNTAX + string server_info(); + +DESCRIPTION + This function returns a string describing the server we are talking + to. It has the form "servername/serverversion" (like the HTTP protocol + description) and is most useful in conjunction with the generic SQL-server + module. + +SEE ALSO + /precompiled/sql + +============================================================================ +NAME + host_info - describe the connection we're using + +SYNTAX + string host_info(); + +DESCRIPTION + This function returns a string describing what host are we talking to, + and how (TCP/IP or UNIX sockets). + +============================================================================ +NAME + create_db - create a new database + +SYNTAX + void create_db(string dbname); + +DESCRIPTION + This function creates a new database with the given name (assuming we + have enough permissions to do this). + +============================================================================ +NAME + drop_db - destroy a database + +SYNTAX + void drop_db(string dbname); + +DESCRIPTION + This function destroys a database and all the data it contains (assuming + we have enough permissions to do so). USE WITH CAUTION! + +============================================================================ +NAME + shutdown - shut down a server + +SYNTAX + void shutdown(); + +DESCRIPTION + This function shuts a SQL-server down. + +============================================================================ +NAME + reload_acl - reloads a server's ACL + +SYNTAX + void reload_acl(); + +DESCRIPTION + This function forces a server to reload its ACLs. + +============================================================================ +NAME + affected_rows - show how many rows were 'touched' by last operation + +SYNTAX + int affected_rows(); + +DESCRIPTION + This function returns how many rows in the database were affected by + our last SQL query. + +NOTA BENE + This function is availible only if we're supporting mSQL server version 2 + or later. (That means: if the includes and library of version 2 of mSQL + were availible when the module was compiled). + +============================================================================ +NAME + list_index - show the index structure for a table + +SYNTAX + array(string) list_index(string tablename, string indexname); + +DESCRIPTION + This function returns an array describing the index structure for the + given table and index name, as defined by the non-standard SQL query + 'create index' (see the mSQL documentation for further informations). + More than one index can be created for a table. There's currently NO way + to have a listing of the indexes defined for a table (blame it on + the mSQL API). + +NOTA BENE + This function is availible if support the module is for mSQL version 2 + or later. + +============================================================================ +EXTERNAL INFLUENCES on the module. + These ones do not depend on my implementation really, they're just part + of the mSQL C API. They take the form of certain environment variables + which, if defined in the environment of the pike interpreter, influence + the interface's behavior. Those are "MSQL_TCP_PORT" which forces the + server to connect to a port other than the default, "MSQL_UNIX_PORT", same + as above, only referring to the UNIX domain sockets. If you built your + mSQL server with the default setttings, you shouldn't worry about these. + The variable MINERVA_DEBUG can be used to debug the mSQL API (you + shouldn't worry about this either). Refer to the mSQL documentation + for further details. + +NOTES, TODOs AND OTHER THINGS + This program is my first attempt at touching the Pike source code, so + I may well have messed everything up. I hope I didn't. + + Note that although the database engine should theoretically + store any kind of values, the interface seems to be quite text-oriented: + strings are returned as null-terminated char*, and not as a + (datum,len) couple. For now I followed this trend. I will change this + behavior as soon as I can think of a safe way. + + I didn't make any testsuite for this module, since to test anything it would + require a working mSQL server, and I can't make any assumption + on those... You can try to use the included "test_msql.pike" script, + but you'll possibly have to patch it. + + Also note that THIS MODULE USES BLOCKING I/O to connect to the server. + mSQL should be reasonably fast, but you might want to consider this + particular aspect. It should however be thread-safe. + +THANKS + Many thanks to Henrik Grubbström, without whose help this piece of + code couldn't be nearly as complete as it is. + Also thanks to Frederik Hubinette, for designing such a nice language + as Pike. + +COPYRIGHT + This code and documentation are copyright 1997 Francesco Chemolli + <kinkie@comedia.it>. + It may be copied, modified, redistributed under the terms of the + GNU General Public License, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Pike source. If not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + The mSQL (Mini-SQL) server and its accompanying tools and documentation are + copyright 1993-1996 Hughes Technologies Pty Ltd, and are free to use + for non-commercial entities. For informations about Mini SQL please + consult the Hughes Thechnologies WWW site, at http://hughes.com.au/ + + The Pike programming language is copyright Frederik Hubinette, and is + distributed under the terms of the GNU General Public License. For furhter + information, please consult the Pike WWW site, at http://pike.infovav.se/ diff --git a/src/modules/Msql/msql_config.h.in b/src/modules/Msql/msql_config.h.in new file mode 100644 index 0000000000..bb1bf04953 --- /dev/null +++ b/src/modules/Msql/msql_config.h.in @@ -0,0 +1,23 @@ +/* $Id: msql_config.h.in,v 1.1.1.1 1997/10/14 21:55:42 grubba Exp $ */ +#undef STDC_HEADERS + +#undef HAVE_MSQL_H + +#undef HAVE_LIBMSQL + +#undef HAVE_MSQLLISTINDEX + +#undef HAVE_PIKE_SVALUE_H + +/* End of autoconfigurable section */ +#undef HAVE_MSQL + +#ifdef HAVE_MSQL_H +#ifdef HAVE_LIBMSQL +#define HAVE_MSQL +#endif +#endif + +#ifdef HAVE_MSQLLISTINDEX +#define MSQL_VERSION_2 +#endif diff --git a/src/modules/Msql/msqlmod.c b/src/modules/Msql/msqlmod.c new file mode 100644 index 0000000000..48d48915ac --- /dev/null +++ b/src/modules/Msql/msqlmod.c @@ -0,0 +1,783 @@ +/* + * This code is (C) Francesco Chemolli, 1997. + * You may use, modify and redistribute it freely under the terms + * of the GNU General Public License, version 2. + * $Id: msqlmod.c,v 1.1.1.1 1997/10/14 21:55:42 grubba Exp $ + * + * This versione is intended for Pike/0.5 and later. + * It won't compile under older versions of the Pike interpreter. + */ + +/* All this code is pretty useless if we don't have a msql library...*/ +#include "msql_config.h" +#ifdef HAVE_MSQL + +/* #define MSQL_DEBUG 1 */ + +#ifdef MSQL_DEBUG +#include <stdio.h> +#endif + +#include <string.h> +#include <stdlib.h> + +#include <machine.h> +#include <svalue.h> +#include <threads.h> +#include <global.h> +#include <interpret.h> +#include <program.h> +#include <array.h> +#include <mapping.h> +#include <stralloc.h> +#include <builtin_functions.h> +#include <module_support.h> + +RCSID("$Id: msqlmod.c,v 1.1.1.1 1997/10/14 21:55:42 grubba Exp $"); + +#ifdef _REENTRANT +MUTEX_T pike_msql_mutex; +#define MSQL_LOCK() mt_lock(&pike_msql_mutex) +#define MSQL_UNLOCK() mt_unlock(&pike_msql_mutex) +#else +#define MSQL_LOCK() /**/ +#define MSQL_UNLOCK() /**/ +#endif + +#include <msql.h> + +static char * decode_msql_type (int msql_type) +{ + switch (msql_type) { + case INT_TYPE: return "int"; + case CHAR_TYPE: return "char"; + case REAL_TYPE: return "real"; + case IDENT_TYPE: return "ident"; + case NULL_TYPE: return "null"; +#ifdef MSQL_VERSION_2 + case TEXT_TYPE: return "text"; + case UINT_TYPE: return "unsigned int"; + case IDX_TYPE: return "index"; + case SYSVAR_TYPE: return "sysvar"; + case ANY_TYPE: return "any"; +#endif + default: return "unknown"; + } +}; + +struct msql_my_data +{ + int socket; /* the communication socket between us and the database engine. */ + int db_selected:1; /*flag: if we selected a database*/ + int connected:1; /*flag: we connected to a server*/ + struct pike_string *error_msg; +#ifdef MSQL_VERSION_2 + int affected; +#endif +}; + +#define THIS ((struct msql_my_data *) fp->current_storage) + +static void msql_object_created (struct object *o) +{ + THIS->connected=0; + THIS->db_selected=0; + THIS->error_msg=NULL; +} + +static void msql_object_destroyed (struct object * o) +{ + if (THIS->connected) { + int socket=THIS->socket; + THREADS_ALLOW(); + MSQL_LOCK(); + msqlClose(socket); + MSQL_UNLOCK(); + THREADS_DISALLOW(); + } + if (THIS->error_msg) + free_string(THIS->error_msg); +} + +static void report_error (void) +{ + if (THIS->error_msg) + free_string(THIS->error_msg); + THIS->error_msg=make_shared_string(msqlErrMsg); + /* msqlErrMsg is really a char[160] in mSQL/2.0, but I don't want + to take any chances, even if I'm wasting some time here. */ +} + +static void do_select_db(char * dbname) +{ + /* NOTICE: We're assuming we're connected. CHECK before calling! */ + int status,socket=THIS->socket; + + THREADS_ALLOW(); + MSQL_LOCK(); + status=msqlSelectDB(socket,dbname); + MSQL_UNLOCK(); + THREADS_DISALLOW(); + + if (status==-1) + { + THIS->db_selected=0; + report_error(); + error("Could not select database.\n"); + } + THIS->db_selected=1; + return; +} + +/* void shutdown() */ +static void do_shutdown (INT32 args) +/* Notice: the msqlShutdown() function is undocumented. I'll have to go + through the source to find how to report errors.*/ +{ + int status=0,socket=THIS->socket; + check_all_args("Msql->shutdown",args,0); + pop_n_elems(args); + + if (!THIS->connected) + error ("Not connected to any server.\n"); + + THREADS_ALLOW(); + MSQL_LOCK(); + status=msqlShutdown(socket); + if (status>=0) + msqlClose(socket); /*DBserver is shut down, might as well close */ + MSQL_UNLOCK(); + THREADS_DISALLOW(); + if (status<0) { + report_error(); + error ("Error while shutting down the DBserver, connection not closed.\n"); + } + THIS->connected=0; + THIS->db_selected=0; +} + +/* void reload_acl() */ +static void do_reload_acl (INT32 args) +/* Undocumented mSQL function. */ +{ + int socket,status=0; + check_all_args("Msql->reload_acl",args,0); + pop_n_elems(args); + if (!THIS->connected) + error ("Not connected to any server.\n"); + + socket=THIS->socket; + THREADS_ALLOW(); + MSQL_LOCK(); + status=msqlReloadAcls(socket); + MSQL_UNLOCK(); + THREADS_DISALLOW(); + if (status<0) { + report_error(); + error ("Could not reload ACLs.\n"); + } +} + +/* void create (void|string dbserver, void|string dbname, + void|string username, void|string passwd) */ +static void msql_mod_create (INT32 args) +{ + struct pike_string * arg1=NULL, *arg2=NULL; + int sock, status; + + check_all_args("Msql->create",args, + BIT_STRING|BIT_VOID,BIT_STRING|BIT_VOID,BIT_STRING|BIT_VOID, + BIT_STRING|BIT_VOID,0); + + if (args) + if (sp[-args].u.string->len) + arg1=sp[-args].u.string; + if (args >1) + if (sp[1-args].u.string->len) + arg2=sp[1-args].u.string; + + /*Okay. We had one or two arguments, and we must connect to a server + and if needed select a database. + First off let's check whether we are already connected. In this case, + disconnect.*/ + if (THIS->connected) + { + msqlClose (THIS->socket); + THIS->connected=0; + THIS->db_selected=0; + } + + THREADS_ALLOW(); + MSQL_LOCK(); + /* Warning! If there were no args, we're deferencing a NULL pointer!*/ + if (!arg1 || !strcmp (arg1->str,"localhost")) + sock=msqlConnect(NULL); + else + sock=msqlConnect(arg1->str); + MSQL_UNLOCK(); + THREADS_DISALLOW(); + + if (sock==-1) { + THIS->db_selected=0; + THIS->connected=0; + report_error(); + error("Error while connecting to mSQL server.\n"); + } + THIS->socket=sock; + THIS->connected=1; + if (!arg2) + return; + do_select_db(arg2->str); + pop_n_elems(args); +} + +/* array list_dbs(void|string wild) */ +static void do_list_dbs (INT32 args) +{ + m_result * result; + m_row row; + int fields,numrows=0,socket=THIS->socket; + + check_all_args("Msql->list_dbs",args,BIT_STRING|BIT_VOID,0); + + if (!THIS->connected) + error ("Not connected.\n"); + if (args>0 && sp[-args].u.string->len) + /* We have a glob. We should pop the arg and push it again for a later + * call to glob() */ + ; + else { + pop_n_elems(args); + args=0; /*Let's use args as a flag. If args==0, no globbing.*/ + } + + THREADS_ALLOW(); + MSQL_LOCK(); + result=msqlListDBs(socket); + MSQL_UNLOCK(); + THREADS_DISALLOW(); + + if (!result) { + f_aggregate(0); /*empty array if no databases*/ + return; + } + while (row=msqlFetchRow(result)) /*it's fast, we're in RAM*/ + { + numrows++; + push_text(row[0]); + } + f_aggregate(numrows); + msqlFreeResult(result); + if (args) + f_glob(2); + return; +} + +/* array list_tables(void|string wild) */ +static void do_list_tables (INT32 args) + /* ARGH! There's much code duplication here... but the subtle differences + * between the various functions make it impervious to try to generalize..*/ +{ + m_result * result; + m_row row; + int fields,numrows=0,socket=THIS->socket; + + check_all_args ("Msql->list_tables",args,BIT_STRING|BIT_VOID,0); + + if (!THIS->db_selected) + error ("No database selected.\n"); + + if (args>0 && sp[-args].u.string->len) + /* We have a glob. We should pop the arg and push it again for a later + * call to glob() */ + ; + else { + pop_n_elems(args); + args=0; /*Let's use args as a flag. If args==0, no globbing.*/ + } + + THREADS_ALLOW(); + MSQL_LOCK(); + result=msqlListTables(socket); + MSQL_UNLOCK(); + THREADS_DISALLOW(); + + if (!result) { + f_aggregate(0); /*empty array if no databases*/ + return; + } + while (row=msqlFetchRow(result)) + { + numrows++; + push_text(row[0]); + } + f_aggregate(numrows); + msqlFreeResult(result); + if (args) + f_glob(2); + return; +} + +/* void select_db(string dbname) */ +static void select_db(INT32 args) +{ + struct pike_string * arg; + int status; + + check_all_args("Msql->select_db",args,BIT_STRING,0); + if (!THIS->connected) + error ("Not connected.\n"); + arg=sp[-args].u.string; + do_select_db(arg->str); + pop_n_elems(args); +} + +/* array(mapping(string:mixed)) query (string sqlquery) */ +static void do_query (INT32 args) +{ + int status, num_fields,num_rows,j,k,tmp_socket,*types,*duplicate_names_map; + m_result * result; + m_field * current_field; + m_row row; + struct array *field_names, *retval; + char * query, ** names; + + check_all_args("Msql->query",args,BIT_STRING,0); + + if (!THIS->connected) + error("Must connect to database server before querying it.\n"); + if (!THIS->db_selected) + error("Must select database before querying it.\n"); + + tmp_socket=THIS->socket; + query=sp[-args].u.string->str; + +#ifdef MSQL_DEBUG + printf ("MSQLMod: Query is\"%s\"\n",query); +#endif + + THREADS_ALLOW(); + MSQL_LOCK(); + status=msqlQuery(tmp_socket,query); + MSQL_UNLOCK(); + THREADS_DISALLOW(); + + if (status==-1) { + report_error(); + error("Error in SQL query.\n"); + } + +#ifdef MSQL_VERSION_2 + THIS->affected=status; +#endif + pop_n_elems(args); + /*We have what we want. We need to construct the returned structure*/ + THREADS_ALLOW(); + MSQL_LOCK(); + result=msqlStoreResult(); + MSQL_UNLOCK(); + THREADS_DISALLOW(); + if (!result || !(num_rows=msqlNumRows(result)) ) { + f_aggregate(0); + return; + } + + /*First off: we need to know the field names and types + *in their correct order, and store them in arrays for further analysis + */ + num_fields=msqlNumFields(result); + types = malloc (sizeof(int)*num_fields); + duplicate_names_map = malloc (sizeof(int)*num_fields); + names = malloc (sizeof(char*)*num_fields); + if (!types || !duplicate_names_map || !names) + error ("Memory exhausted.\n"); + + /* initialize support structure for duplicate column names */ + for (j=0;j<num_fields;j++) + duplicate_names_map[j]=0; + + /* Find duplicate column names */ + for (j=0;j<num_fields;j++) { + current_field=msqlFetchField(result); + names[j]=current_field->name; + } + for (j=0;j<num_fields;j++) + for (k=j+1;k<num_fields;k++) + if (!strcmp(names[j],names[k])) { + duplicate_names_map[j]=1; + duplicate_names_map[k]=1; + } + + /* Reset field cursor */ + msqlFieldSeek(result,0); + + /* create array containing column names */ + for (j=0;j<num_fields;j++) { + current_field=msqlFetchField(result); + if (!current_field) + error ("Huh? Weird! Null field returned at index %d.\n",j); + if (duplicate_names_map[j]) { + /* If we have a clashing column name we need to prepend "tablename."*/ + push_text (current_field->table); + push_text ("."); + push_text(current_field->name); + f_add(3); + } + else + push_text(current_field->name); + types[j]=current_field->type; + } + + field_names=aggregate_array(num_fields); + + /* Let's fetch the rows and accumulate them as mappings on the stack. */ + for (j=0;j<num_rows;j++) + { + struct array * this_row; + struct mapping * this_result; + int k; + row=msqlFetchRow(result); + for (k=0;k<num_fields;k++) { + if (!row[k]) { + push_int(0); + continue; + } + switch (types[k]) { +#ifdef MSQL_VERSION_2 + case UINT_TYPE: /*this may lose the MSB and overflow. + Is htere a better way?*/ +#endif + case INT_TYPE: push_int(atoi(row[k])); break; + case REAL_TYPE: push_float(atof(row[k])); break; + default: push_text(row[k]); break; + } + } + this_row=aggregate_array(num_fields); + this_result=mkmapping(field_names,this_row); + push_mapping(this_result); /*we're putting mappings on the stack*/ + free_array(this_row); + } + + /* Wrap it up and free the used memory */ + f_aggregate(num_rows); /* aggregate and push the resulting array */ + free_array(field_names); + free(types); + free(duplicate_names_map); + free(names); + msqlFreeResult(result); +} + +/* string server_info() */ +static void do_info (INT32 args) +{ + char * info; + + check_all_args("Msql->info",args,0); + pop_n_elems(args); + if (!THIS->connected) + error ("Not connected.\n"); + push_text("msql/"); + THREADS_ALLOW(); + MSQL_LOCK(); + info=msqlGetServerInfo(); + MSQL_UNLOCK(); + THREADS_DISALLOW(); + push_text(info); + f_add(2); + return; +} + +/* string host_info() */ +static void do_host_info (INT32 args) +{ + check_all_args("Msql->host_info",args,0); + pop_n_elems(args); + if (!THIS->connected) + error ("Not connected.\n"); + /*it's local to the client library. Not even worth allowing + context switches*/ + push_text(msqlGetHostInfo()); + return; +} + +/* string error() */ +static void do_error (INT32 args) +{ + check_all_args("Msql->error",args,0); + pop_n_elems(args); + if (THIS->error_msg) + push_string(THIS->error_msg); + return; +} + +/* void create_db (string dbname) */ +static void do_create_db (INT32 args) +{ + int dbresult; + char * dbname; + int socket; + + check_all_args("Msql->create_db",args,BIT_STRING,0); + + if (!THIS->connected) + error("Not connected.\n"); + dbname = sp[-args].u.string->str; + socket=THIS->socket; + THREADS_ALLOW(); + MSQL_LOCK(); + dbresult=msqlCreateDB(socket,dbname); + MSQL_UNLOCK(); + THREADS_DISALLOW(); + if (dbresult==-1) { + report_error(); + error ("Could not create database.\n"); + } + pop_n_elems(args); +} + +/* void drop_db (string dbname) */ +static void do_drop_db (INT32 args) +{ + int dbresult; + char * dbname; + int socket; + + check_all_args("Msql->drop_db",args,BIT_STRING,0); + + if (!THIS->connected) + error("Not connected.\n"); + dbname = sp[-args].u.string->str; + socket=THIS->socket; + THREADS_ALLOW(); + MSQL_LOCK(); + dbresult=msqlDropDB(socket,dbname); + MSQL_UNLOCK(); + THREADS_DISALLOW(); + if (dbresult==-1) { + report_error(); + error ("Could not drop database.\n"); + } + pop_n_elems(args); + return; +} + +/* mapping(string:array(string)) list_fields (string table) */ +static void do_list_fields (INT32 args) +{ + m_result * result; + m_field * field; + char * table; + int fields, j, socket=THIS->socket; + + check_all_args("Msql->list_fields",args,BIT_STRING,0); + if (!THIS->connected) + error ("Not connected.\n"); + if (!THIS->db_selected) + error ("Must select a db first.\n"); + table=sp[-args].u.string->str; +#ifdef MSQL_DEBUG + printf ("list_fields: table=%s(%d)\n",sp[-args].u.string->str,sp[-args].u.string->len); +#endif + + THREADS_ALLOW(); + MSQL_LOCK(); + result=msqlListFields(socket,table); + MSQL_UNLOCK(); + THREADS_DISALLOW(); + pop_n_elems(args); + + if (!result) { + report_error(); + error ("No fields information.\n"); + } + + fields = msqlNumFields(result); + if (!fields) + error ("No such table.\n"); + + for (j=0;j<fields;j++) + { + int flagsnum=0; + field=msqlFetchField(result); + push_text(field->name); + + push_text("type"); + push_text(decode_msql_type(field->type)); + push_text("length"); + push_int(field->length); + push_text("table"); + push_text(field->table); + push_text("flags"); +#ifdef IS_UNIQUE + if (IS_UNIQUE(field->flags)) { + push_text("unique"); + flagsnum++; + } +#endif +#ifdef IS_NOT_NULL + if (IS_NOT_NULL(field->flags)) { + push_text("not_null"); + flagsnum++; + } +#endif +#ifdef IS_PRI_KEY + if (IS_PRI_KEY(field->flags)) { + push_text("primary_key"); + flagsnum++; + } +#endif + f_aggregate_multiset(flagsnum); + f_aggregate_mapping(8); /*must aggregate on the fields above, except name*/ + } + f_aggregate_mapping(fields*2); + + msqlFreeResult(result); + return; +} + +#ifdef MSQL_VERSION_2 +/* int affected_rows() */ +static void do_affected_rows (INT32 args) +{ + check_all_args("Msql->affected_rows",args,0); + pop_n_elems(args); + push_int(THIS->affected); + return; +} + +/* array list_index(string tablename, string indexname) */ +static void do_list_index (INT32 args) +{ + char * arg1, *arg2; + m_result * result; + m_row row; + int sock, rows, j; + + check_all_args("Msql->list_index",args,BIT_STRING,BIT_STRING,0); + if (!THIS->db_selected) + error ("No database selected.\n"); + arg1=sp[-args].u.string->str; + arg2=sp[1-args].u.string->str; + sock=THIS->socket; + THREADS_ALLOW(); + MSQL_LOCK(); + result=msqlListIndex(sock,arg1,arg2); + MSQL_UNLOCK(); + THREADS_DISALLOW(); + pop_n_elems(args); + if (!result || !(rows=msqlNumRows(result)) ) { + f_aggregate(0); + return; + } + msqlFetchRow(result); /*The first one is the internal type, useless*/ + rows--; + for (j=0; j<rows; j++) + { + row=msqlFetchRow(result); + push_text(row[0]); + } + f_aggregate(rows); + return; +} +#endif + +void pike_module_init(void) +{ + start_new_program(); + add_storage(sizeof(struct msql_my_data)); + + set_init_callback (msql_object_created); + set_exit_callback (msql_object_destroyed); + + add_function("create",msql_mod_create, + "function(void|string,void|string,void|string,void|string:void)", + OPT_EXTERNAL_DEPEND); + /* 1st arg: hostname or "localhost", 2nd arg: dbname or nothing + * CAN raise exception if there is no server listening, or no database + * To connect using the UNIX socket instead of a localfunction use the + * hostname "localhost", or use no argument. It will use UNIX sockets. + * Third and fourth argument are currently ignored, since mSQL doesn't + * support user/passwd authorization. The user will be the owner of + * the current process. + */ + + add_function("select_db",select_db,"function(string:void)", + OPT_EXTERNAL_DEPEND); + /* if no db selected by connect, does it now. + * CAN raise an exception if there's no such database or we haven't selected + * an host. + */ + + add_function("query",do_query, + "function(string:array(mapping(string:mixed)))", + OPT_ASSIGNMENT|OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND|OPT_RETURN); + /* Gets an SQL query, and returns an array of the results, one element + * for each result line, each row is a mapping with the column name as + * index and the data as value. + * CAN raise excaptions if there's no active database. + */ + + add_function ("list_dbs",do_list_dbs, + "function(void|string:array(string))", + OPT_ASSIGNMENT|OPT_EXTERNAL_DEPEND|OPT_RETURN); + /* Lists the tables contained in the selected database. */ + + add_function ("list_tables",do_list_tables, + "function(void:array(string))", + OPT_ASSIGNMENT|OPT_EXTERNAL_DEPEND|OPT_RETURN); + /* Lists the tables contained in the selected database. */ + + add_function ("list_fields", do_list_fields, + "function(string:mapping(string:array(mixed)))", + OPT_RETURN|OPT_EXTERNAL_DEPEND); + /* Returns information on the the fields of the given table of the current + database */ + + add_function ("error",do_error, "function(void:void|string)", + OPT_RETURN|OPT_EXTERNAL_DEPEND); + /* return the last error reported by the server. */ + + add_function ("server_info", do_info, "function(void:string)", + OPT_RETURN|OPT_EXTERNAL_DEPEND); + /* Returns "msql/<server_version>" */ + + add_function ("host_info", do_host_info, "function(void:string)", + OPT_EXTERNAL_DEPEND|OPT_RETURN); + /* Returns information on the connection type and such */ + + add_function ("create_db", do_create_db, "function(string:void)", + OPT_EXTERNAL_DEPEND); + /* creates a new database with the name as argument */ + + add_function ("drop_db", do_drop_db, "function(string:void)", + OPT_EXTERNAL_DEPEND); + /* destroys a database and its contents */ + + add_function ("shutdown", do_shutdown, "function(void:void)", + OPT_EXTERNAL_DEPEND); + /* Shuts the server down */ + + add_function ("reload_acl", do_reload_acl, "function(void:void)", + OPT_EXTERNAL_DEPEND); + /* Reloads the ACL for the DBserver */ + +#ifdef MSQL_VERSION_2 + add_function ("affected_rows", do_affected_rows, "function(void:int)", + OPT_RETURN|OPT_EXTERNAL_DEPEND); + /* Returns the number of rows 'touched' by last query */ + /* UNTESTED */ + + add_function ("list_index", do_list_index, "function(string,string:array)", + OPT_EXTERNAL_DEPEND); + /* Returns the index structure on the specified table */ + /* UNTESTED */ +#endif + + end_class("msql",0); +} + +#else /*HAVE_MSQL*/ +void pike_module_init(void) {} +#endif /*HAVE_MSQL*/ + +void pike_module_exit (void) { } diff --git a/src/modules/Msql/test_msqlmod.pike b/src/modules/Msql/test_msqlmod.pike new file mode 100755 index 0000000000..65ff5df3d2 --- /dev/null +++ b/src/modules/Msql/test_msqlmod.pike @@ -0,0 +1,33 @@ +#!../../pike +#include <msql.h> + +int main() +{ + array result; + string table; + mixed err; + + object db; + + db=msql(); + werror (sprintf ("error :'%O'\n",db->error())); + + result=db->list_dbs(); + write(sprintf("Databases: %O\n",result)); + write ("connecting to database "+result[0]+"\n"); + db->select_db(result[0]); + result = db->list_tables(); + write(sprintf("Tables: %O\n",result)); + table=result[0]; + write("*** Dumping fields of table: "+table+"\n"); + catch {result=db->list_fields(table);}; + write(sprintf("%O\n",result)); + write ("*** Dumping contents of table:"+table+"\n"); + write(sprintf("%O\n",db->query("",""))); + err = catch (write(sprintf("%O\n",db->query("Select * from "+table)))); + if (err) + write (db->error()); + write(sprintf("%O\n",db->error())); + write(db->server_info()+"\n"); + write(db->host_info()+"\n"); +} diff --git a/src/modules/Msql/testsuite.in b/src/modules/Msql/testsuite.in new file mode 100644 index 0000000000..e69de29bb2 -- GitLab