├── VERSION.in ├── etc ├── get_version.ml ├── converr.pl ├── errmsg.h ├── errors.inc └── mysqld_error.txt ├── META ├── .gitignore ├── configure.ac ├── Makefile.in ├── Makefile.msvc ├── demo2.ml ├── config.h.in ├── demo.ml ├── README ├── CHANGES ├── install-sh ├── mysql.mli ├── mysql.ml ├── COPYING ├── mysql_stubs.c └── OCamlMakefile /VERSION.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_VERSION@ 2 | -------------------------------------------------------------------------------- /etc/get_version.ml: -------------------------------------------------------------------------------- 1 | let () = print_endline (input_line (open_in "VERSION")) 2 | -------------------------------------------------------------------------------- /META: -------------------------------------------------------------------------------- 1 | name="mysql" 2 | description="OCaml bindings to MySQL" 3 | requires="" 4 | archive(byte) = "mysql.cma" 5 | archive(native) = "mysql.cmxa" 6 | plugin(native) = "mysql.cmxs" 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | doc/ 2 | ._d/ 3 | ._bcdi/ 4 | ._ncdi/ 5 | Makefile 6 | VERSION 7 | config.h 8 | config.log 9 | config.status 10 | autom4te.cache 11 | *.byte 12 | *.native 13 | *.cm[aiox] 14 | *.cmx[as] 15 | *.o 16 | *.obj 17 | *.so 18 | *.dll 19 | *.lib 20 | *.a 21 | -------------------------------------------------------------------------------- /etc/converr.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | use strict; 3 | 4 | open OUTFILE, ">", "errors.inc" or die "Couldn't open errors.inc for writing!\n";; 5 | open INFILE, "<", "mysqld_error.txt" or die "Couldn't open mysqld_error.txt for reading!\n"; 6 | 7 | print OUTFILE "(* Auto-generated on ", scalar gmtime, " from MySQL headers. *)\n"; 8 | print OUTFILE "type error_code = "; 9 | 10 | my @types; 11 | my @codes; 12 | while () { 13 | chomp; 14 | if (/^#define ER_(\w+)\s+(\d+)/o) { 15 | my $err = lc $1; 16 | my $code = $2; 17 | push @codes, $code; 18 | push @types, ucfirst $err; 19 | } 20 | } 21 | 22 | close INFILE; 23 | open INFILE, "<", "errmsg.h" or die "Couldn't open errmsg.h for reading!\n"; 24 | while () { 25 | chomp; 26 | if (/^#define CR_(\w+)\s+(\d+)/o) { 27 | my $err = lc $1; 28 | my $code = $2; 29 | next if $err eq "max_error" or $err eq "min_error"; 30 | push @codes, $code; 31 | push @types, ucfirst $err; 32 | } 33 | } 34 | close INFILE; 35 | 36 | my %unique_types = map { $_ => 0 } @types; 37 | my @unique_types = keys %unique_types; 38 | @unique_types = sort @unique_types; 39 | 40 | print OUTFILE join(" | ", @unique_types), "\n\n"; 41 | 42 | my $i = 0; 43 | print OUTFILE "let error_of_int code = match code with\n"; 44 | foreach my $type (@types) { 45 | print OUTFILE "| $codes[$i] -> $type\n"; 46 | $i++; 47 | } 48 | 49 | print OUTFILE "| _ -> Unknown_error\n"; 50 | 51 | close OUTFILE; 52 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT(ocaml-mysql,1.2.4) 2 | 3 | AC_CHECKING([for ocaml compiler and tools]) 4 | AC_CHECK_PROG(ocamlc,ocamlc,ocamlc) 5 | AC_CHECK_PROG(ocamlopt,ocamlopt,ocamlopt) 6 | AC_CHECK_PROG(ocamlfind,ocamlfind,ocamlfind) 7 | 8 | AC_MSG_CHECKING([whether ocamlopt supports natdynlink]) 9 | AS_IF([`grep -q "NATDYNLINK=true" $(ocamlc -where)/Makefile.config`],[CAN_NATDYNLINK=yes],[CAN_NATDYNLINK=no]) 10 | AC_MSG_RESULT([$CAN_NATDYNLINK]) 11 | AC_SUBST(CAN_NATDYNLINK) 12 | 13 | AC_PROG_INSTALL 14 | AC_SUBST(INSTALL) 15 | 16 | AC_CHECKING([for C compiler]) 17 | AC_PROG_CC() 18 | 19 | AC_CHECKING([for C header files]) 20 | AC_HEADER_STDC() 21 | 22 | AC_MSG_CHECKING([for mysql includes]) 23 | # pkg-config as last resort because debian buster pkg-config flags are wrong 24 | MYSQL_INCLUDE=`mysql_config --include || mariadb_config --include || pkg-config mariadb --cflags` 25 | AS_IF([ test "$?" -eq 0 ],[CPPFLAGS="$MYSQL_INCLUDE $CPPFLAGS"],) 26 | AC_MSG_RESULT([$MYSQL_INCLUDE]) 27 | 28 | AC_MSG_CHECKING([for mysql libraries]) 29 | MYSQL_LINK_FLAGS=`mysql_config --libs || mariadb_config --libs || pkg-config mariadb --libs` 30 | AS_IF([ test "$?" -eq 0 ],[LDFLAGS="$MYSQL_LINK_FLAGS $LDFLAGS"],) 31 | AC_MSG_RESULT([$MYSQL_LINK_FLAGS]) 32 | 33 | AC_CHECK_HEADERS([mysql.h mysql/mysql.h], [break]) 34 | if test "$ac_cv_header_mysql_mysql_h" != "yes" -a "$ac_cv_header_mysql_h" != "yes"; then 35 | AC_MSG_ERROR([mysql.h not found]) 36 | fi 37 | 38 | AC_CONFIG_HEADERS([config.h]) 39 | AC_OUTPUT(Makefile) 40 | AC_OUTPUT(VERSION) 41 | -------------------------------------------------------------------------------- /Makefile.in: -------------------------------------------------------------------------------- 1 | export NO_CUSTOM=1 2 | 3 | SOURCES=mysql.mli mysql.ml mysql_stubs.c 4 | RESULT=mysql 5 | VERSION=@PACKAGE_VERSION@ 6 | 7 | LIBINSTALL_FILES=$(wildcard *.mli *.cmi *.cma *.cmx *.cmxa *.a *.so *.cmxs) 8 | 9 | CFLAGS=@CFLAGS@ @DEFS@ -Wall -Wextra 10 | CPPFLAGS=@CPPFLAGS@ 11 | CLIBS=$(foreach x, $(filter -l%, @LDFLAGS@), $(patsubst -l%,%,${x})) 12 | LDFLAGS=$(filter-out -l%, @LDFLAGS@) 13 | OCAMLMKLIB_FLAGS=$(LDFLAGS) 14 | OCAMLFIND_INSTFLAGS=-patch-version "$(VERSION)" 15 | 16 | build: all opt 17 | all: byte-code-library 18 | 19 | ifeq (@CAN_NATDYNLINK@,yes) 20 | CMXS=mysql.cmxs 21 | 22 | clean:: 23 | rm -f mysql.cmxs 24 | endif 25 | 26 | opt: native-code-library $(CMXS) 27 | reallyall: byte-code-library native-code-library $(CMXS) htdoc 28 | 29 | install: libinstall 30 | uninstall: libuninstall 31 | 32 | demos: reallyall 33 | ocamlc -custom -I . mysql.cma demo.ml -o demo.byte 34 | $(OCAMLOPT) -I . mysql.cmxa demo.ml -o demo.native 35 | ocamlc -custom -I . -thread unix.cma threads.cma mysql.cma demo2.ml -o demo2.byte 36 | $(OCAMLOPT) -I . -thread unix.cmxa threads.cmxa mysql.cmxa demo2.ml -o demo2.native 37 | 38 | mysql.cmxs: mysql.cmx 39 | $(OCAMLOPT) -shared $(foreach flag,$(LDFLAGS), -ccopt ${flag}) mysql_stubs.o $(foreach lib,$(CLIBS), -cclib -l${lib}) -o mysql.cmxs mysql.cmx 40 | 41 | clean-demos: 42 | rm -f demo*.{byte,native,cm*,o} 43 | 44 | cleanall: clean-demos clean-doc clean 45 | 46 | -include OCamlMakefile 47 | 48 | NAME=ocaml-mysql-$(VERSION) 49 | 50 | .PHONY: release 51 | release: 52 | git tag -a -m $(VERSION) v$(VERSION) 53 | git archive --prefix=$(NAME)/ v$(VERSION) | gzip > $(NAME).tar.gz 54 | gpg -a -b $(NAME).tar.gz 55 | -------------------------------------------------------------------------------- /Makefile.msvc: -------------------------------------------------------------------------------- 1 | 2 | VERSION=$(shell ocaml etc/get_version.ml) 3 | 4 | MYSQL_DIR=C:/my/contrib/mysql-connector-c-noinstall-6.0.2-win32 5 | 6 | # dynamic linking - will need libmysql.dll at runtime 7 | CLIBS=$(MYSQL_DIR)/lib/libmysql.lib 8 | # static linking 9 | #CLIBS=$(MYSQL_DIR)/lib/mysqlclient.lib 10 | 11 | CFLAGS=/W3 /WL /wd4996 /I$(MYSQL_DIR)/include 12 | LIBINSTALL_FILES=$(wildcard mysql.mli mysql.cm* mysql.a libmysql_stubs.a dllmysql_stubs.so mysql.lib libmysql_stubs.lib dllmysql_stubs.dll) 13 | 14 | OCAMLMKLIB=ocamlmklib -ocamlc ocamlc -ocamlopt ocamlopt -verbose 15 | 16 | build: all 17 | all: mysql.cma mysql.cmxa 18 | 19 | mysql.cma mysql.cmxa: mysql.ml mysql.mli mysql_stubs.c 20 | ocamlc -c -ccopt "$(CFLAGS)" mysql_stubs.c 21 | ocamlc -c mysql.mli 22 | ocamlc -c mysql.ml 23 | ocamlopt -c mysql.ml 24 | $(OCAMLMKLIB) -o mysql -oc mysql_stubs mysql.cmo mysql.cmx mysql_stubs.obj $(CLIBS) 25 | 26 | demos: all 27 | ocamlc -custom -I . mysql.cma demo.ml -o demo.byte 28 | ocamlopt -I . mysql.cmxa demo.ml -o demo.native 29 | ocamlc -custom -I . -thread unix.cma threads.cma mysql.cma demo2.ml -o demo2.byte 30 | ocamlopt -I . -thread unix.cmxa threads.cmxa mysql.cmxa demo2.ml -o demo2.native 31 | 32 | install: all 33 | ocamlfind install -patch-version "$(VERSION)" mysql META $(LIBINSTALL_FILES) 34 | 35 | uninstall: 36 | ocamlfind remove mysql 37 | 38 | htdoc: doc/index.html 39 | 40 | doc/index.html: mysql.mli 41 | -mkdir doc 42 | ocamldoc -html -d doc $< 43 | 44 | clean: 45 | del $(wildcard *.cm* *.o *.a *.so *.obj *.lib *.dll *.byte* *.native*) 46 | 47 | #release: 48 | # git archive --format=tar --prefix=ocaml-mysql-$(VERSION)/ v$(VERSION) | gzip > ocaml-mysql-$(VERSION).tar.gz 49 | -------------------------------------------------------------------------------- /demo2.ml: -------------------------------------------------------------------------------- 1 | (** 2 | Demo for the Mysql.Prepared module 3 | *) 4 | 5 | open Printf 6 | module P = Mysql.Prepared 7 | 8 | (* 9 | Verify safe GC interaction (see CAML_TEST_GC_SAFE in mysql_stubs.c) 10 | For such test need strings on heap, not statically allocated atoms, hence String.copy 11 | *) 12 | let (_:Thread.t) = Thread.create (fun () -> 13 | let i = ref 0 in 14 | while true do Gc.compact(); incr i; if !i mod 100 = 0 then (print_char '.'; flush stdout) done) () 15 | 16 | let s = String.copy 17 | 18 | let db = Mysql.quick_connect ~database:(s "test") ~user:(s "root") () 19 | 20 | let (_:Mysql.result) = Mysql.exec db (s "CREATE TABLE test(id INT, v VARCHAR(10)) ENGINE=MEMORY") 21 | let () = 22 | let insert = P.create db (s "INSERT INTO test VALUES (?,?)") in 23 | for i = 10 to 15 do 24 | ignore (P.execute insert [|string_of_int i; sprintf "value %d" i|]) 25 | done; 26 | for i = 16 to 20 do 27 | ignore (P.execute_null insert [|Some (string_of_int i); None|]) 28 | done; 29 | P.close insert 30 | 31 | let () = 32 | let rec loop t = 33 | match P.fetch t with 34 | | Some arr -> Array.iter (function Some s -> printf "%s " s | None -> print_string " ") arr; print_endline ""; loop t 35 | | None -> () 36 | in 37 | let select = P.create db (s "SELECT * FROM test WHERE id > ?") in 38 | print_endline "> 13"; 39 | loop (P.execute select [|s "13"|]); 40 | print_endline "> 19"; 41 | loop (P.execute select [|s "19"|]); 42 | print_endline "> 20"; 43 | loop (P.execute select [|s "20"|]); 44 | P.close select; 45 | print_endline "done all"; 46 | () 47 | 48 | let (_:Mysql.result) = Mysql.exec db (s "DROP TABLE test") 49 | 50 | let () = Mysql.disconnect db 51 | -------------------------------------------------------------------------------- /config.h.in: -------------------------------------------------------------------------------- 1 | /* config.h.in. Generated from configure.ac by autoheader. */ 2 | 3 | /* Define to 1 if you have the header file. */ 4 | #undef HAVE_INTTYPES_H 5 | 6 | /* Define to 1 if you have the header file. */ 7 | #undef HAVE_MEMORY_H 8 | 9 | /* Define to 1 if you have the header file. */ 10 | #undef HAVE_MYSQL_H 11 | 12 | /* Define to 1 if you have the header file. */ 13 | #undef HAVE_MYSQL_MYSQL_H 14 | 15 | /* Define to 1 if you have the header file. */ 16 | #undef HAVE_STDINT_H 17 | 18 | /* Define to 1 if you have the header file. */ 19 | #undef HAVE_STDLIB_H 20 | 21 | /* Define to 1 if you have the header file. */ 22 | #undef HAVE_STRINGS_H 23 | 24 | /* Define to 1 if you have the header file. */ 25 | #undef HAVE_STRING_H 26 | 27 | /* Define to 1 if you have the header file. */ 28 | #undef HAVE_SYS_STAT_H 29 | 30 | /* Define to 1 if you have the header file. */ 31 | #undef HAVE_SYS_TYPES_H 32 | 33 | /* Define to 1 if you have the header file. */ 34 | #undef HAVE_UNISTD_H 35 | 36 | /* Define to the address where bug reports for this package should be sent. */ 37 | #undef PACKAGE_BUGREPORT 38 | 39 | /* Define to the full name of this package. */ 40 | #undef PACKAGE_NAME 41 | 42 | /* Define to the full name and version of this package. */ 43 | #undef PACKAGE_STRING 44 | 45 | /* Define to the one symbol short name of this package. */ 46 | #undef PACKAGE_TARNAME 47 | 48 | /* Define to the home page for this package. */ 49 | #undef PACKAGE_URL 50 | 51 | /* Define to the version of this package. */ 52 | #undef PACKAGE_VERSION 53 | 54 | /* Define to 1 if you have the ANSI C header files. */ 55 | #undef STDC_HEADERS 56 | -------------------------------------------------------------------------------- /demo.ml: -------------------------------------------------------------------------------- 1 | (* 2 | $Id: demo.ml 1.1 Thu, 23 Feb 2006 14:13:22 -0800 shawnw $ 3 | 4 | Simple demo for the Mysql module. 5 | *) 6 | 7 | open Mysql 8 | 9 | (* login informations *) 10 | 11 | let db = quick_connect ~database:"test" () 12 | 13 | let table = 14 | [ ("one" , 1, 1.0) 15 | ; ("two" , 2, 2.0) 16 | ; ("three" , 3, 3.0) 17 | ; (",':-" , 4, 4.0) 18 | ] 19 | 20 | 21 | let mk_table () = 22 | let _r = exec db "create table caml (a char(64), b int, c float)" in 23 | db 24 | 25 | let fill_table c = 26 | let ml2values (a,b,c) = values [ml2str a; ml2int b; ml2float c] in 27 | let insert values = "insert into caml values " ^ values in 28 | let rec loop = function 29 | | [] -> () 30 | | x::xs -> ( ignore (exec c (insert (ml2values x))) 31 | ; loop xs 32 | ) 33 | in 34 | loop table 35 | 36 | let read_table c = 37 | let r = exec c "select * from caml" in 38 | let col = column r in 39 | let row x = ( not_null str2ml (col ~key:"a" ~row:x) 40 | , not_null int2ml (col ~key:"b" ~row:x) 41 | , not_null float2ml (col ~key:"c" ~row:x) 42 | ) in 43 | let rec loop = function 44 | | None -> [] 45 | | Some x -> row x :: loop (fetch r) 46 | in 47 | loop (fetch r) 48 | 49 | 50 | let main () = 51 | let c = mk_table () in 52 | ( fill_table c 53 | ; ignore (read_table c) 54 | ; ignore (exec c "drop table caml") 55 | ; disconnect c 56 | ) 57 | 58 | 59 | let _ = Printexc.print main () 60 | -------------------------------------------------------------------------------- /etc/errmsg.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB 2 | 3 | This library is free software; you can redistribute it and/or 4 | modify it under the terms of the GNU Library General Public 5 | License as published by the Free Software Foundation; either 6 | version 2 of the License, or (at your option) any later version. 7 | 8 | This library is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | Library General Public License for more details. 12 | 13 | You should have received a copy of the GNU Library General Public 14 | License along with this library; if not, write to the Free 15 | Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, 16 | MA 02111-1307, USA */ 17 | 18 | /* Error messages for mysql clients */ 19 | /* error messages for the demon is in share/language/errmsg.sys */ 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | void init_client_errs(void); 25 | extern const char *client_errors[]; /* Error messages */ 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | 30 | #define CR_MIN_ERROR 2000 /* For easier client code */ 31 | #define CR_MAX_ERROR 2999 32 | #if defined(OS2) && defined( MYSQL_SERVER) 33 | #define CER(X) client_errors[(X)-CR_MIN_ERROR] 34 | #else 35 | #define ER(X) client_errors[(X)-CR_MIN_ERROR] 36 | #endif 37 | #define CLIENT_ERRMAP 2 /* Errormap used by my_error() */ 38 | 39 | #define CR_UNKNOWN_ERROR 2000 40 | #define CR_SOCKET_CREATE_ERROR 2001 41 | #define CR_CONNECTION_ERROR 2002 42 | #define CR_CONN_HOST_ERROR 2003 43 | #define CR_IPSOCK_ERROR 2004 44 | #define CR_UNKNOWN_HOST 2005 45 | #define CR_SERVER_GONE_ERROR 2006 46 | #define CR_VERSION_ERROR 2007 47 | #define CR_OUT_OF_MEMORY 2008 48 | #define CR_WRONG_HOST_INFO 2009 49 | #define CR_LOCALHOST_CONNECTION 2010 50 | #define CR_TCP_CONNECTION 2011 51 | #define CR_SERVER_HANDSHAKE_ERR 2012 52 | #define CR_SERVER_LOST 2013 53 | #define CR_COMMANDS_OUT_OF_SYNC 2014 54 | #define CR_NAMEDPIPE_CONNECTION 2015 55 | #define CR_NAMEDPIPEWAIT_ERROR 2016 56 | #define CR_NAMEDPIPEOPEN_ERROR 2017 57 | #define CR_NAMEDPIPESETSTATE_ERROR 2018 58 | #define CR_CANT_READ_CHARSET 2019 59 | #define CR_NET_PACKET_TOO_LARGE 2020 60 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | OCaml-MySQL -- MySQL access for OCaml 3 | ************************************* 4 | 5 | http://ocaml-mysql.forge.ocamlcore.org/ 6 | ======================================= 7 | Based on a release by: 8 | Shawn Wagner 9 | shawnw@speakeasy.org 10 | http://raevnos.pennmush.org/code/ocaml-mysql 11 | ============================================ 12 | Based on a release by: 13 | Christian Lindig 14 | lindig@eecs.harvard.edu 15 | http://www.eecs.harvard.edu/~lindig 16 | =================================== 17 | 18 | 19 | ocaml-mysql is a package for OCaml that provides access to mysql 20 | databases. It consists of low-level C bindings to libmysqlclient and a 21 | module Mysql intended for application development. 22 | 23 | 24 | 1 Building 25 | *=*=*=*=*=* 26 | 27 | 28 | Compiling this package from sources requires the following software to 29 | be installed on your system: 30 | 31 | 32 | 1. ocaml 3.09 or above. 33 | 2. findlib 34 | 3. The mysql client library and header files. 35 | 4. An ANSI C compiler like gcc. 36 | 37 | A configure script is provided which should help to find the 38 | particular files. In case configure fails, edit the setting in the 39 | config file directly. This also applies when the C compiler fails to 40 | find some include file. However, you should first try the obvious: 41 | 42 | << 43 | % ./configure 44 | % make 45 | % make opt 46 | % make install 47 | >> 48 | 49 | This creates the mysql libraries. 50 | 51 | 1' Building on windows 52 | *=*=*=*=*=*=*=*=*=*=*=* 53 | 54 | 55 | Compiling this package from sources requires the following software to be 56 | installed on your system: 57 | 58 | 1. ocaml 3.11 or above with accompanying C compiler setup (msvc or mingw) 59 | 2. findlib 60 | 3. MySQL Connector/C 61 | 4. GNU Make 62 | 63 | ocaml/msvc : 64 | 65 | Copy Makefile.msvc to Makefile and edit MYSQL_DIR variable to point to the root 66 | of installed MySQL Connector/C distribution. Afterwards run `make` to build mysql library, 67 | `make demos` to compile examples and `make install` to install with ocamlfind. 68 | 69 | ocaml/mingw: 70 | 71 | Some mingw reimp versions can't process libmysql.lib import library. 72 | In such cases try to rename this file into "$M/lib/libmysql.a" without 73 | any processing by reimp. 74 | 75 | $ export P="c:/Program Files/MySQL/MySQL Connector C 6.0.2" 76 | $ export M="c:/mingw" 77 | $ cp "$P/lib/opt/libmysql.lib" "$M/lib/" 78 | $ (cd "$M/lib" && reimp libmysql.lib && mv liblibmysql.a libmysql.a) 79 | $ CPPFLAGS="-I$P/include" ./configure 80 | $ make all opt demos 81 | 82 | 83 | 2 Troubleshooting 84 | *=*=*=*=*=*=*=*=*= 85 | 86 | 87 | If compilation fails this is most probably because include or library 88 | files were not found. The configure looks in a number of places but may 89 | fail to find the files on your system. In case you have GNU Autoconf 90 | installed you can take a look at the configure.in and add more 91 | directories to search: 92 | 93 | << 94 | AC_CHECKING(for MySQL header files) 95 | dnl 96 | dirs="/usr/local/include 97 | /usr/local/mysql/include 98 | /usr/include 99 | /usr/include/mysql" 100 | >> 101 | 102 | Do not forget to create a new configure script by invoking autoconf. 103 | Please send a patch back to the author. 104 | 105 | 106 | 3. Documentation 107 | *=*=*=*=*=*=*=*= 108 | 109 | 110 | Check the interface files, or doc/mysql/html/index.html (generated with `make htdoc`). 111 | Reading the mysql documentation should help, too. 112 | Two small demos are available. Build them with `make demos`. 113 | 114 | Note: The library can be used in multithreaded ocaml programs without 115 | blocking threads during i/o with the database server. 116 | Since MySQL 5.5 it is safe to share database handle between threads in most scenarios, see 117 | http://dev.mysql.com/doc/refman/5.5/en/c-api-threaded-clients.html 118 | 119 | 4 Copying 120 | *=*=*=*=*= 121 | 122 | You are encouraged to distribute this code under the terms of the 123 | Lesser GNU Public License. See the file COPYING for details. 124 | 125 | 5. Authors 126 | *=*=*=*=*= 127 | 128 | Previous maintainers: 129 | Christian Lindig 130 | Shawn Wagner 131 | 132 | Current maintainer: 133 | ygrek 134 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | * Tue Nov 12 2019 (1.2.4) 2 | * Auto-initialize library in Mysql.escape 3 | 4 | * Tue Nov 12 2019 (1.2.3) 5 | * Support build with libmariadb (using mariadb_config) 6 | 7 | * Thu Sep 21 2017 (1.2.2) 8 | * Build and install cmxs 9 | * Support build with libmariadbclient 10 | 11 | * Sat Jul 23 2016 (1.2.1) 12 | * Fix crash in fetch_fields 13 | * Fix build with OCaml 4.03 14 | * Fix error message in Mysql.Prepared.create 15 | 16 | * Tue Mar 10 2015 (1.2.0) 17 | * Get rid of Camlp4 dependency 18 | * Use mysql_config in configure 19 | 20 | * Thu Oct 16 2014 (1.1.3) 21 | * + OPT_FOUND_ROWS (Dmitry Grebeniuk) 22 | * configure: build with percona 23 | 24 | * Tue Nov 19 2013 (1.1.2) 25 | * Mysql.Prepared.execute_null (Gregory Bellier) 26 | 27 | * Sat May 19 2012 (1.1.1) 28 | * Support build with ocaml/msvc and ocaml/mingw (Dmitry Grebeniuk) 29 | * Update build tools (Dmitry Grebeniuk) 30 | * OCaml 3.12 compatibility 31 | * Mysql.Prepared: documentation comments 32 | * Mysql.Prepared.result_metadata (Hezekiah M. Carty) 33 | * Mysql.quick_connect: optional unix socket path 34 | * Mysql.quick_connect: optional connection options 35 | 36 | * Sat Dec 26 2009 (1.1.0) 37 | * Project moved to http://ocaml-mysql.forge.ocamlcore.org 38 | * Fix crash bugs (mainly in GC & threads interaction) 39 | * Trigger GC less often (Mysql.connect and Mysql.execute) 40 | * Mysql.real_escape (Debian patch) 41 | * Mysql.set_charset 42 | * Preliminary support for prepared statements (Mysql.Prepared) 43 | 44 | * Thu Feb 23 2006 (1.0.4) 45 | * Build fixes. 46 | * Can be used in threaded programs, but only by one thread at a time. 47 | 48 | * Tue Jan 27 2004 (1.0.3) 49 | * You now have to 'make opt' or 'make reallyall' to get the native-code 50 | library. Only the bytecode version is built by default. 51 | * Support for systems without dynamic linking. See Makefile.conf 52 | * Mysql.client_info now takes unit as an argument, not a dbd. 53 | 54 | * Fri Oct 24 2003 (1.0.2) 55 | * Now released under the LGPL 56 | * Mysql.insert_id (Basile Starynkevitch) 57 | 58 | * Fri Sep 26 2003 (1.0.1) 59 | * configure fixes (Gleb Semenov) 60 | * Uses OcamlMakefile 61 | * No longer builds a toplevel, as it's easy to use findlib to load the module. 62 | 63 | * Mon May 12 2003 (1.0.0) 64 | * Bumped the version number; officially stable 65 | 66 | * Sat Apr 12 2003 (0.9.1) 67 | * Some functions would cause segfaults in the toplevel (Michael Wohlwen) 68 | * configure improvements (Blair Zajac) 69 | * Many typos fixed (Blair Zajac) 70 | * Mysql.map* and Mysql.iter* play nicely with empty result sets (Blair Zajac) 71 | 72 | * Thu Feb 20 2003 (0.9.0) 73 | * Big version bump, as this has been stable for years. 1.0.0 soon. 74 | * Dynamic linking works at long last. 75 | * Link with -lz as newer versions of mysql than what I was using need it. 76 | Reported by several people. 77 | * Updated my testing copy of mysql to 3.23.55. 78 | * New html documentation in the doc directory. Generated by ocamldoc instead 79 | of ocamlweb. 80 | * New quick_connect, quick_change and defaults convenience functions. 81 | 82 | * Mon Nov 04 2002 (0.1.5) 83 | * configure dies if findlib isn't installed. 84 | * Fixed problem with the datetime type (David Fox) 85 | * Added support for the decimal type (David Fox) 86 | 87 | * Wed July 24 2002 (0.1.4) 88 | * More memory-allocation fixes by Francois Rouaix 89 | 90 | * Sat July 13 2002 (0.1.3) 91 | * Fix bugs in Mysql.fetch_fields. Reported by Emiliano Leporati 92 | It doesn't crash when trying to use the results, and it returns 93 | field array option instead of field option array option. The old 94 | type didn't really make sense. 95 | 96 | * Sun July 07 2002 (0.1.2) 97 | * Some memory-allocation fixes by Artem Prisyaznuk 98 | 99 | * Sun May 05 2002 (0.1.1) 100 | * Better support for blob types 101 | * Mysql.escape now works right on strings with embedded nuls. 102 | These two changes make it possible to store marshaled ocaml data in 103 | a mysql database and recover it later. Suggested by Maxence Guesdon 104 | 105 | * Mon Apr 22 2002 106 | * Many bug fixes related to files being left out of the tarball, 107 | and a completely messed up configure.in. 108 | * Uses findlib. 109 | * Note: Dynamic linking of the C stubs doesn't work. Yet. 110 | 111 | * Tue Apr 9 18:09:27 PDT 2002 112 | + Lazy-ass makes the first public release of the new version 113 | 114 | * ~2001 115 | + Project taken over by Shawn Wagner. 116 | Extensive re-writing. 117 | Lots of new features. 118 | 119 | * Sun Dec 5 14:15:29 CET 1999 120 | + Documentation now in LaTeX format; is translated to html and 121 | Ascii. 122 | 123 | * Mon Nov 22 20:06:16 CET 1999 (Version 0.0.5) 124 | + fetch_by_name renamed to column 125 | + improved exception in case db_exec fails 126 | + doc target for Makefile - generates mysql.html from mysql.mli 127 | + ocaml.css added (for mysql.html) 128 | 129 | 130 | * Sat Nov 20 21:08:00 CET 1999 131 | + Initial version 132 | 133 | -------------------------------------------------------------------------------- /install-sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # install - install a program, script, or datafile 4 | # This comes from X11R5 (mit/util/scripts/install.sh). 5 | # 6 | # Copyright 1991 by the Massachusetts Institute of Technology 7 | # 8 | # Permission to use, copy, modify, distribute, and sell this software and its 9 | # documentation for any purpose is hereby granted without fee, provided that 10 | # the above copyright notice appear in all copies and that both that 11 | # copyright notice and this permission notice appear in supporting 12 | # documentation, and that the name of M.I.T. not be used in advertising or 13 | # publicity pertaining to distribution of the software without specific, 14 | # written prior permission. M.I.T. makes no representations about the 15 | # suitability of this software for any purpose. It is provided "as is" 16 | # without express or implied warranty. 17 | # 18 | # Calling this script install-sh is preferred over install.sh, to prevent 19 | # `make' implicit rules from creating a file called install from it 20 | # when there is no Makefile. 21 | # 22 | # This script is compatible with the BSD install script, but was written 23 | # from scratch. It can only install one file at a time, a restriction 24 | # shared with many OS's install programs. 25 | 26 | 27 | # set DOITPROG to echo to test this script 28 | 29 | # Don't use :- since 4.3BSD and earlier shells don't like it. 30 | doit="${DOITPROG-}" 31 | 32 | 33 | # put in absolute paths if you don't have them in your path; or use env. vars. 34 | 35 | mvprog="${MVPROG-mv}" 36 | cpprog="${CPPROG-cp}" 37 | chmodprog="${CHMODPROG-chmod}" 38 | chownprog="${CHOWNPROG-chown}" 39 | chgrpprog="${CHGRPPROG-chgrp}" 40 | stripprog="${STRIPPROG-strip}" 41 | rmprog="${RMPROG-rm}" 42 | mkdirprog="${MKDIRPROG-mkdir}" 43 | 44 | transformbasename="" 45 | transform_arg="" 46 | instcmd="$mvprog" 47 | chmodcmd="$chmodprog 0755" 48 | chowncmd="" 49 | chgrpcmd="" 50 | stripcmd="" 51 | rmcmd="$rmprog -f" 52 | mvcmd="$mvprog" 53 | src="" 54 | dst="" 55 | dir_arg="" 56 | 57 | while [ x"$1" != x ]; do 58 | case $1 in 59 | -c) instcmd="$cpprog" 60 | shift 61 | continue;; 62 | 63 | -d) dir_arg=true 64 | shift 65 | continue;; 66 | 67 | -m) chmodcmd="$chmodprog $2" 68 | shift 69 | shift 70 | continue;; 71 | 72 | -o) chowncmd="$chownprog $2" 73 | shift 74 | shift 75 | continue;; 76 | 77 | -g) chgrpcmd="$chgrpprog $2" 78 | shift 79 | shift 80 | continue;; 81 | 82 | -s) stripcmd="$stripprog" 83 | shift 84 | continue;; 85 | 86 | -t=*) transformarg=`echo $1 | sed 's/-t=//'` 87 | shift 88 | continue;; 89 | 90 | -b=*) transformbasename=`echo $1 | sed 's/-b=//'` 91 | shift 92 | continue;; 93 | 94 | *) if [ x"$src" = x ] 95 | then 96 | src=$1 97 | else 98 | # this colon is to work around a 386BSD /bin/sh bug 99 | : 100 | dst=$1 101 | fi 102 | shift 103 | continue;; 104 | esac 105 | done 106 | 107 | if [ x"$src" = x ] 108 | then 109 | echo "install: no input file specified" 110 | exit 1 111 | else 112 | true 113 | fi 114 | 115 | if [ x"$dir_arg" != x ]; then 116 | dst=$src 117 | src="" 118 | 119 | if [ -d $dst ]; then 120 | instcmd=: 121 | chmodcmd="" 122 | else 123 | instcmd=mkdir 124 | fi 125 | else 126 | 127 | # Waiting for this to be detected by the "$instcmd $src $dsttmp" command 128 | # might cause directories to be created, which would be especially bad 129 | # if $src (and thus $dsttmp) contains '*'. 130 | 131 | if [ -f $src -o -d $src ] 132 | then 133 | true 134 | else 135 | echo "install: $src does not exist" 136 | exit 1 137 | fi 138 | 139 | if [ x"$dst" = x ] 140 | then 141 | echo "install: no destination specified" 142 | exit 1 143 | else 144 | true 145 | fi 146 | 147 | # If destination is a directory, append the input filename; if your system 148 | # does not like double slashes in filenames, you may need to add some logic 149 | 150 | if [ -d $dst ] 151 | then 152 | dst="$dst"/`basename $src` 153 | else 154 | true 155 | fi 156 | fi 157 | 158 | ## this sed command emulates the dirname command 159 | dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` 160 | 161 | # Make sure that the destination directory exists. 162 | # this part is taken from Noah Friedman's mkinstalldirs script 163 | 164 | # Skip lots of stat calls in the usual case. 165 | if [ ! -d "$dstdir" ]; then 166 | defaultIFS=' 167 | ' 168 | IFS="${IFS-${defaultIFS}}" 169 | 170 | oIFS="${IFS}" 171 | # Some sh's can't handle IFS=/ for some reason. 172 | IFS='%' 173 | set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` 174 | IFS="${oIFS}" 175 | 176 | pathcomp='' 177 | 178 | while [ $# -ne 0 ] ; do 179 | pathcomp="${pathcomp}${1}" 180 | shift 181 | 182 | if [ ! -d "${pathcomp}" ] ; 183 | then 184 | $mkdirprog "${pathcomp}" 185 | else 186 | true 187 | fi 188 | 189 | pathcomp="${pathcomp}/" 190 | done 191 | fi 192 | 193 | if [ x"$dir_arg" != x ] 194 | then 195 | $doit $instcmd $dst && 196 | 197 | if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && 198 | if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && 199 | if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && 200 | if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi 201 | else 202 | 203 | # If we're going to rename the final executable, determine the name now. 204 | 205 | if [ x"$transformarg" = x ] 206 | then 207 | dstfile=`basename $dst` 208 | else 209 | dstfile=`basename $dst $transformbasename | 210 | sed $transformarg`$transformbasename 211 | fi 212 | 213 | # don't allow the sed command to completely eliminate the filename 214 | 215 | if [ x"$dstfile" = x ] 216 | then 217 | dstfile=`basename $dst` 218 | else 219 | true 220 | fi 221 | 222 | # Make a temp file name in the proper directory. 223 | 224 | dsttmp=$dstdir/_inst.$$_ 225 | 226 | # Move or copy the file name to the temp name 227 | 228 | $doit $instcmd $src $dsttmp && 229 | 230 | trap "rm -f ${dsttmp}" 0 && 231 | 232 | # and set any options; do chmod last to preserve setuid bits 233 | 234 | # If any of these fail, we abort the whole thing. If we want to 235 | # ignore errors from any of these, just make sure not to ignore 236 | # errors from the above "$doit $instcmd $src $dsttmp" command. 237 | 238 | if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && 239 | if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && 240 | if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && 241 | if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && 242 | 243 | # Now rename the file to the real destination. 244 | 245 | $doit $rmcmd -f $dstdir/$dstfile && 246 | $doit $mvcmd $dsttmp $dstdir/$dstfile 247 | 248 | fi && 249 | 250 | 251 | exit 0 252 | -------------------------------------------------------------------------------- /etc/errors.inc: -------------------------------------------------------------------------------- 1 | (* Auto-generated on Thu Feb 20 06:10:26 2003 from MySQL headers. *) 2 | type error_code = Aborting_connection | Access_denied_error | Alter_info | Bad_db_error | Bad_field_error | Bad_host_error | Bad_null_error | Bad_table_error | Blob_cant_have_default | Blob_key_without_length | Blob_used_as_key | Blobs_and_no_terminated | Cant_create_db | Cant_create_file | Cant_create_table | Cant_create_thread | Cant_delete_file | Cant_drop_field_or_key | Cant_find_dl_entry | Cant_find_system_rec | Cant_find_udf | Cant_get_stat | Cant_get_wd | Cant_initialize_udf | Cant_lock | Cant_open_file | Cant_open_library | Cant_read_charset | Cant_read_dir | Cant_remove_all_fields | Cant_reopen_table | Cant_set_wd | Checkread | Columnaccess_denied_error | Commands_out_of_sync | Con_count_error | Conn_host_error | Connection_error | Db_create_exists | Db_drop_delete | Db_drop_exists | Db_drop_rmdir | Dbaccess_denied_error | Delayed_cant_change_lock | Delayed_insert_table_locked | Disk_full | Dup_entry | Dup_fieldname | Dup_key | Dup_keyname | Dup_unique | Empty_query | Error_on_close | Error_on_read | Error_on_rename | Error_on_write | Field_specified_twice | File_exists_error | File_not_found | File_used | Filsort_abort | Forcing_close | Form_not_found | Function_not_defined | Get_errno | Got_signal | Grant_wrong_host_or_user | Handshake_error | Hashchk | Host_is_blocked | Host_not_privileged | Illegal_grant_for_table | Illegal_ha | Insert_info | Insert_table_used | Invalid_default | Invalid_group_func_use | Invalid_use_of_null | Ipsock_error | Key_column_does_not_exits | Key_not_found | Kill_denied_error | Load_info | Localhost_connection | Mix_of_group_func_and_fields | Multiple_pri_key | Namedpipe_connection | Namedpipeopen_error | Namedpipesetstate_error | Namedpipewait_error | Net_error_on_write | Net_fcntl_error | Net_packet_too_large | Net_packets_out_of_order | Net_read_error | Net_read_error_from_pipe | Net_read_interrupted | Net_uncompress_error | Net_write_interrupted | Nisamchk | No | No_db_error | No_raid_compiled | No_such_index | No_such_table | No_such_thread | No_tables_used | No_unique_logfile | Non_uniq_error | Nonexisting_grant | Nonexisting_table_grant | Nonuniq_table | Normal_shutdown | Not_allowed_command | Not_form_file | Not_keyfile | Null_column_in_index | Old_keyfile | Open_as_readonly | Out_of_memory | Out_of_resources | Out_of_sortmemory | Outofmemory | Parse_error | Password_anonymous_user | Password_no_match | Password_not_allowed | Primary_cant_have_null | Ready | Record_file_full | Regexp_error | Requires_primary_key | Server_gone_error | Server_handshake_err | Server_lost | Server_shutdown | Shutdown_complete | Socket_create_error | Stack_overrun | Syntax_error | Table_cant_handle_auto_increment | Table_cant_handle_blob | Table_exists_error | Table_must_have_columns | Table_not_locked | Table_not_locked_for_write | Tableaccess_denied_error | Tcp_connection | Textfile_not_readable | Too_big_fieldlength | Too_big_rowsize | Too_big_select | Too_big_set | Too_long_ident | Too_long_key | Too_long_string | Too_many_delayed_threads | Too_many_fields | Too_many_key_parts | Too_many_keys | Too_many_rows | Too_many_tables | Udf_exists | Udf_no_paths | Unexpected_eof | Unknown_character_set | Unknown_com_error | Unknown_error | Unknown_host | Unknown_procedure | Unknown_table | Unsupported_extension | Update_info | Update_without_key_in_safe_mode | Version_error | Wrong_auto_key | Wrong_column_name | Wrong_db_name | Wrong_field_spec | Wrong_field_terminators | Wrong_field_with_group | Wrong_group_field | Wrong_host_info | Wrong_key_column | Wrong_mrg_table | Wrong_outer_join | Wrong_paramcount_to_procedure | Wrong_parameters_to_procedure | Wrong_sub_key | Wrong_sum_select | Wrong_table_name | Wrong_value_count | Wrong_value_count_on_row | Yes 3 | 4 | let error_of_int code = match code with 5 | | 1000 -> Hashchk 6 | | 1001 -> Nisamchk 7 | | 1002 -> No 8 | | 1003 -> Yes 9 | | 1004 -> Cant_create_file 10 | | 1005 -> Cant_create_table 11 | | 1006 -> Cant_create_db 12 | | 1007 -> Db_create_exists 13 | | 1008 -> Db_drop_exists 14 | | 1009 -> Db_drop_delete 15 | | 1010 -> Db_drop_rmdir 16 | | 1011 -> Cant_delete_file 17 | | 1012 -> Cant_find_system_rec 18 | | 1013 -> Cant_get_stat 19 | | 1014 -> Cant_get_wd 20 | | 1015 -> Cant_lock 21 | | 1016 -> Cant_open_file 22 | | 1017 -> File_not_found 23 | | 1018 -> Cant_read_dir 24 | | 1019 -> Cant_set_wd 25 | | 1020 -> Checkread 26 | | 1021 -> Disk_full 27 | | 1022 -> Dup_key 28 | | 1023 -> Error_on_close 29 | | 1024 -> Error_on_read 30 | | 1025 -> Error_on_rename 31 | | 1026 -> Error_on_write 32 | | 1027 -> File_used 33 | | 1028 -> Filsort_abort 34 | | 1029 -> Form_not_found 35 | | 1030 -> Get_errno 36 | | 1031 -> Illegal_ha 37 | | 1032 -> Key_not_found 38 | | 1033 -> Not_form_file 39 | | 1034 -> Not_keyfile 40 | | 1035 -> Old_keyfile 41 | | 1036 -> Open_as_readonly 42 | | 1037 -> Outofmemory 43 | | 1038 -> Out_of_sortmemory 44 | | 1039 -> Unexpected_eof 45 | | 1040 -> Con_count_error 46 | | 1041 -> Out_of_resources 47 | | 1042 -> Bad_host_error 48 | | 1043 -> Handshake_error 49 | | 1044 -> Dbaccess_denied_error 50 | | 1045 -> Access_denied_error 51 | | 1046 -> No_db_error 52 | | 1047 -> Unknown_com_error 53 | | 1048 -> Bad_null_error 54 | | 1049 -> Bad_db_error 55 | | 1050 -> Table_exists_error 56 | | 1051 -> Bad_table_error 57 | | 1052 -> Non_uniq_error 58 | | 1053 -> Server_shutdown 59 | | 1054 -> Bad_field_error 60 | | 1055 -> Wrong_field_with_group 61 | | 1056 -> Wrong_group_field 62 | | 1057 -> Wrong_sum_select 63 | | 1058 -> Wrong_value_count 64 | | 1059 -> Too_long_ident 65 | | 1060 -> Dup_fieldname 66 | | 1061 -> Dup_keyname 67 | | 1062 -> Dup_entry 68 | | 1063 -> Wrong_field_spec 69 | | 1064 -> Parse_error 70 | | 1065 -> Empty_query 71 | | 1066 -> Nonuniq_table 72 | | 1067 -> Invalid_default 73 | | 1068 -> Multiple_pri_key 74 | | 1069 -> Too_many_keys 75 | | 1070 -> Too_many_key_parts 76 | | 1071 -> Too_long_key 77 | | 1072 -> Key_column_does_not_exits 78 | | 1073 -> Blob_used_as_key 79 | | 1074 -> Too_big_fieldlength 80 | | 1075 -> Wrong_auto_key 81 | | 1076 -> Ready 82 | | 1077 -> Normal_shutdown 83 | | 1078 -> Got_signal 84 | | 1079 -> Shutdown_complete 85 | | 1080 -> Forcing_close 86 | | 1081 -> Ipsock_error 87 | | 1082 -> No_such_index 88 | | 1083 -> Wrong_field_terminators 89 | | 1084 -> Blobs_and_no_terminated 90 | | 1085 -> Textfile_not_readable 91 | | 1086 -> File_exists_error 92 | | 1087 -> Load_info 93 | | 1088 -> Alter_info 94 | | 1089 -> Wrong_sub_key 95 | | 1090 -> Cant_remove_all_fields 96 | | 1091 -> Cant_drop_field_or_key 97 | | 1092 -> Insert_info 98 | | 1093 -> Insert_table_used 99 | | 1094 -> No_such_thread 100 | | 1095 -> Kill_denied_error 101 | | 1096 -> No_tables_used 102 | | 1097 -> Too_big_set 103 | | 1098 -> No_unique_logfile 104 | | 1099 -> Table_not_locked_for_write 105 | | 1100 -> Table_not_locked 106 | | 1101 -> Blob_cant_have_default 107 | | 1102 -> Wrong_db_name 108 | | 1103 -> Wrong_table_name 109 | | 1104 -> Too_big_select 110 | | 1105 -> Unknown_error 111 | | 1106 -> Unknown_procedure 112 | | 1107 -> Wrong_paramcount_to_procedure 113 | | 1108 -> Wrong_parameters_to_procedure 114 | | 1109 -> Unknown_table 115 | | 1110 -> Field_specified_twice 116 | | 1111 -> Invalid_group_func_use 117 | | 1112 -> Unsupported_extension 118 | | 1113 -> Table_must_have_columns 119 | | 1114 -> Record_file_full 120 | | 1115 -> Unknown_character_set 121 | | 1116 -> Too_many_tables 122 | | 1117 -> Too_many_fields 123 | | 1118 -> Too_big_rowsize 124 | | 1119 -> Stack_overrun 125 | | 1120 -> Wrong_outer_join 126 | | 1121 -> Null_column_in_index 127 | | 1122 -> Cant_find_udf 128 | | 1123 -> Cant_initialize_udf 129 | | 1124 -> Udf_no_paths 130 | | 1125 -> Udf_exists 131 | | 1126 -> Cant_open_library 132 | | 1127 -> Cant_find_dl_entry 133 | | 1128 -> Function_not_defined 134 | | 1129 -> Host_is_blocked 135 | | 1130 -> Host_not_privileged 136 | | 1131 -> Password_anonymous_user 137 | | 1132 -> Password_not_allowed 138 | | 1133 -> Password_no_match 139 | | 1134 -> Update_info 140 | | 1135 -> Cant_create_thread 141 | | 1136 -> Wrong_value_count_on_row 142 | | 1137 -> Cant_reopen_table 143 | | 1138 -> Invalid_use_of_null 144 | | 1139 -> Regexp_error 145 | | 1140 -> Mix_of_group_func_and_fields 146 | | 1141 -> Nonexisting_grant 147 | | 1142 -> Tableaccess_denied_error 148 | | 1143 -> Columnaccess_denied_error 149 | | 1144 -> Illegal_grant_for_table 150 | | 1145 -> Grant_wrong_host_or_user 151 | | 1146 -> No_such_table 152 | | 1147 -> Nonexisting_table_grant 153 | | 1148 -> Not_allowed_command 154 | | 1149 -> Syntax_error 155 | | 1150 -> Delayed_cant_change_lock 156 | | 1151 -> Too_many_delayed_threads 157 | | 1152 -> Aborting_connection 158 | | 1153 -> Net_packet_too_large 159 | | 1154 -> Net_read_error_from_pipe 160 | | 1155 -> Net_fcntl_error 161 | | 1156 -> Net_packets_out_of_order 162 | | 1157 -> Net_uncompress_error 163 | | 1158 -> Net_read_error 164 | | 1159 -> Net_read_interrupted 165 | | 1160 -> Net_error_on_write 166 | | 1161 -> Net_write_interrupted 167 | | 1162 -> Too_long_string 168 | | 1163 -> Table_cant_handle_blob 169 | | 1164 -> Table_cant_handle_auto_increment 170 | | 1165 -> Delayed_insert_table_locked 171 | | 1166 -> Wrong_column_name 172 | | 1167 -> Wrong_key_column 173 | | 1168 -> Wrong_mrg_table 174 | | 1169 -> Dup_unique 175 | | 1170 -> Blob_key_without_length 176 | | 1171 -> Primary_cant_have_null 177 | | 1172 -> Too_many_rows 178 | | 1173 -> Requires_primary_key 179 | | 1174 -> No_raid_compiled 180 | | 1175 -> Update_without_key_in_safe_mode 181 | | 2000 -> Unknown_error 182 | | 2001 -> Socket_create_error 183 | | 2002 -> Connection_error 184 | | 2003 -> Conn_host_error 185 | | 2004 -> Ipsock_error 186 | | 2005 -> Unknown_host 187 | | 2006 -> Server_gone_error 188 | | 2007 -> Version_error 189 | | 2008 -> Out_of_memory 190 | | 2009 -> Wrong_host_info 191 | | 2010 -> Localhost_connection 192 | | 2011 -> Tcp_connection 193 | | 2012 -> Server_handshake_err 194 | | 2013 -> Server_lost 195 | | 2014 -> Commands_out_of_sync 196 | | 2015 -> Namedpipe_connection 197 | | 2016 -> Namedpipewait_error 198 | | 2017 -> Namedpipeopen_error 199 | | 2018 -> Namedpipesetstate_error 200 | | 2019 -> Cant_read_charset 201 | | 2020 -> Net_packet_too_large 202 | | _ -> Unknown_error 203 | -------------------------------------------------------------------------------- /etc/mysqld_error.txt: -------------------------------------------------------------------------------- 1 | /* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB 2 | This file is public domain and comes with NO WARRANTY of any kind */ 3 | 4 | #define ER_HASHCHK 1000 5 | "hashchk", 6 | #define ER_NISAMCHK 1001 7 | "isamchk", 8 | #define ER_NO 1002 9 | "NO", 10 | #define ER_YES 1003 11 | "YES", 12 | #define ER_CANT_CREATE_FILE 1004 13 | "Can't create file '%-.64s' (errno: %d)", 14 | #define ER_CANT_CREATE_TABLE 1005 15 | "Can't create table '%-.64s' (errno: %d)", 16 | #define ER_CANT_CREATE_DB 1006 17 | "Can't create database '%-.64s'. (errno: %d)", 18 | #define ER_DB_CREATE_EXISTS 1007 19 | "Can't create database '%-.64s'. Database exists", 20 | #define ER_DB_DROP_EXISTS 1008 21 | "Can't drop database '%-.64s'. Database doesn't exist", 22 | #define ER_DB_DROP_DELETE 1009 23 | "Error dropping database (can't delete '%-.64s', errno: %d)", 24 | #define ER_DB_DROP_RMDIR 1010 25 | "Error dropping database (can't rmdir '%-.64s', errno: %d)", 26 | #define ER_CANT_DELETE_FILE 1011 27 | "Error on delete of '%-.64s' (errno: %d)", 28 | #define ER_CANT_FIND_SYSTEM_REC 1012 29 | "Can't read record in system table", 30 | #define ER_CANT_GET_STAT 1013 31 | "Can't get status of '%-.64s' (errno: %d)", 32 | #define ER_CANT_GET_WD 1014 33 | "Can't get working directory (errno: %d)", 34 | #define ER_CANT_LOCK 1015 35 | "Can't lock file (errno: %d)", 36 | #define ER_CANT_OPEN_FILE 1016 37 | "Can't open file: '%-.64s'. (errno: %d)", 38 | #define ER_FILE_NOT_FOUND 1017 39 | "Can't find file: '%-.64s' (errno: %d)", 40 | #define ER_CANT_READ_DIR 1018 41 | "Can't read dir of '%-.64s' (errno: %d)", 42 | #define ER_CANT_SET_WD 1019 43 | "Can't change dir to '%-.64s' (errno: %d)", 44 | #define ER_CHECKREAD 1020 45 | "Record has changed since last read in table '%-.64s'", 46 | #define ER_DISK_FULL 1021 47 | "Disk full (%s). Waiting for someone to free some space....", 48 | #define ER_DUP_KEY 1022 49 | "Can't write, duplicate key in table '%-.64s'", 50 | #define ER_ERROR_ON_CLOSE 1023 51 | "Error on close of '%-.64s' (errno: %d)", 52 | #define ER_ERROR_ON_READ 1024 53 | "Error reading file '%-.64s' (errno: %d)", 54 | #define ER_ERROR_ON_RENAME 1025 55 | "Error on rename of '%-.64s' to '%-.64s' (errno: %d)", 56 | #define ER_ERROR_ON_WRITE 1026 57 | "Error writing file '%-.64s' (errno: %d)", 58 | #define ER_FILE_USED 1027 59 | "'%-.64s' is locked against change", 60 | #define ER_FILSORT_ABORT 1028 61 | "Sort aborted", 62 | #define ER_FORM_NOT_FOUND 1029 63 | "View '%-.64s' doesn't exist for '%-.64s'", 64 | #define ER_GET_ERRNO 1030 65 | "Got error %d from table handler", 66 | #define ER_ILLEGAL_HA 1031 67 | "Table handler for '%-.64s' doesn't have this option", 68 | #define ER_KEY_NOT_FOUND 1032 69 | "Can't find record in '%-.64s'", 70 | #define ER_NOT_FORM_FILE 1033 71 | "Incorrect information in file: '%-.64s'", 72 | #define ER_NOT_KEYFILE 1034 73 | "Incorrect key file for table: '%-.64s'. Try to repair it", 74 | #define ER_OLD_KEYFILE 1035 75 | "Old key file for table '%-.64s'; Repair it!", 76 | #define ER_OPEN_AS_READONLY 1036 77 | "Table '%-.64s' is read only", 78 | #define ER_OUTOFMEMORY 1037 79 | "Out of memory. Restart daemon and try again (needed %d bytes)", 80 | #define ER_OUT_OF_SORTMEMORY 1038 81 | "Out of sort memory. Increase daemon sort buffer size", 82 | #define ER_UNEXPECTED_EOF 1039 83 | "Unexpected eof found when reading file '%-.64s' (errno: %d)", 84 | #define ER_CON_COUNT_ERROR 1040 85 | "Too many connections", 86 | #define ER_OUT_OF_RESOURCES 1041 87 | "Out of memory; Check if mysqld or some other process uses all available memory. If not you may have to use 'ulimit' to allow mysqld to use more memory or you can add more swap space", 88 | #define ER_BAD_HOST_ERROR 1042 89 | "Can't get hostname for your address", 90 | #define ER_HANDSHAKE_ERROR 1043 91 | "Bad handshake", 92 | #define ER_DBACCESS_DENIED_ERROR 1044 93 | "Access denied for user: '%-.32s@%-.64s' to database '%-.64s'", 94 | #define ER_ACCESS_DENIED_ERROR 1045 95 | "Access denied for user: '%-.32s@%-.64s' (Using password: %s)", 96 | #define ER_NO_DB_ERROR 1046 97 | "No Database Selected", 98 | #define ER_UNKNOWN_COM_ERROR 1047 99 | "Unknown command", 100 | #define ER_BAD_NULL_ERROR 1048 101 | "Column '%-.64s' cannot be null", 102 | #define ER_BAD_DB_ERROR 1049 103 | "Unknown database '%-.64s'", 104 | #define ER_TABLE_EXISTS_ERROR 1050 105 | "Table '%-.64s' already exists", 106 | #define ER_BAD_TABLE_ERROR 1051 107 | "Unknown table '%-.64s'", 108 | #define ER_NON_UNIQ_ERROR 1052 109 | "Column: '%-.64s' in %-.64s is ambiguous", 110 | #define ER_SERVER_SHUTDOWN 1053 111 | "Server shutdown in progress", 112 | #define ER_BAD_FIELD_ERROR 1054 113 | "Unknown column '%-.64s' in '%-.64s'", 114 | #define ER_WRONG_FIELD_WITH_GROUP 1055 115 | "'%-.64s' isn't in GROUP BY", 116 | #define ER_WRONG_GROUP_FIELD 1056 117 | "Can't group on '%-.64s'", 118 | #define ER_WRONG_SUM_SELECT 1057 119 | "Statement has sum functions and columns in same statement", 120 | #define ER_WRONG_VALUE_COUNT 1058 121 | "Column count doesn't match value count", 122 | #define ER_TOO_LONG_IDENT 1059 123 | "Identifier name '%-.100s' is too long", 124 | #define ER_DUP_FIELDNAME 1060 125 | "Duplicate column name '%-.64s'", 126 | #define ER_DUP_KEYNAME 1061 127 | "Duplicate key name '%-.64s'", 128 | #define ER_DUP_ENTRY 1062 129 | "Duplicate entry '%-.64s' for key %d", 130 | #define ER_WRONG_FIELD_SPEC 1063 131 | "Incorrect column specifier for column '%-.64s'", 132 | #define ER_PARSE_ERROR 1064 133 | "%s near '%-.80s' at line %d", 134 | #define ER_EMPTY_QUERY 1065 135 | "Query was empty", 136 | #define ER_NONUNIQ_TABLE 1066 137 | "Not unique table/alias: '%-.64s'", 138 | #define ER_INVALID_DEFAULT 1067 139 | "Invalid default value for '%-.64s'", 140 | #define ER_MULTIPLE_PRI_KEY 1068 141 | "Multiple primary key defined", 142 | #define ER_TOO_MANY_KEYS 1069 143 | "Too many keys specified. Max %d keys allowed", 144 | #define ER_TOO_MANY_KEY_PARTS 1070 145 | "Too many key parts specified. Max %d parts allowed", 146 | #define ER_TOO_LONG_KEY 1071 147 | "Specified key was too long. Max key length is %d", 148 | #define ER_KEY_COLUMN_DOES_NOT_EXITS 1072 149 | "Key column '%-.64s' doesn't exist in table", 150 | #define ER_BLOB_USED_AS_KEY 1073 151 | "BLOB column '%-.64s' can't be used in key specification with the used table type", 152 | #define ER_TOO_BIG_FIELDLENGTH 1074 153 | "Too big column length for column '%-.64s' (max = %d). Use BLOB instead", 154 | #define ER_WRONG_AUTO_KEY 1075 155 | "Incorrect table definition; There can only be one auto column and it must be defined as a key", 156 | #define ER_READY 1076 157 | "%s: ready for connections\n", 158 | #define ER_NORMAL_SHUTDOWN 1077 159 | "%s: Normal shutdown\n", 160 | #define ER_GOT_SIGNAL 1078 161 | "%s: Got signal %d. Aborting!\n", 162 | #define ER_SHUTDOWN_COMPLETE 1079 163 | "%s: Shutdown Complete\n", 164 | #define ER_FORCING_CLOSE 1080 165 | "%s: Forcing close of thread %ld user: '%-.32s'\n", 166 | #define ER_IPSOCK_ERROR 1081 167 | "Can't create IP socket", 168 | #define ER_NO_SUCH_INDEX 1082 169 | "Table '%-.64s' has no index like the one used in CREATE INDEX. Recreate the table", 170 | #define ER_WRONG_FIELD_TERMINATORS 1083 171 | "Field separator argument is not what is expected. Check the manual"," 172 | #define ER_BLOBS_AND_NO_TERMINATED 1084 173 | "You can't use fixed rowlength with BLOBs. Please use 'fields terminated by'.", 174 | #define ER_TEXTFILE_NOT_READABLE 1085 175 | "The file '%-.64s' must be in the database directory or be readable by all", 176 | #define ER_FILE_EXISTS_ERROR 1086 177 | "File '%-.80s' already exists", 178 | #define ER_LOAD_INFO 1087 179 | "Records: %ld Deleted: %ld Skipped: %ld Warnings: %ld", 180 | #define ER_ALTER_INFO 1088 181 | "Records: %ld Duplicates: %ld", 182 | #define ER_WRONG_SUB_KEY 1089 183 | "Incorrect sub part key. The used key part isn't a string or the used length is longer than the key part", 184 | #define ER_CANT_REMOVE_ALL_FIELDS 1090 185 | "You can't delete all columns with ALTER TABLE. Use DROP TABLE instead", 186 | #define ER_CANT_DROP_FIELD_OR_KEY 1091 187 | "Can't DROP '%-.64s'. Check that column/key exists", 188 | #define ER_INSERT_INFO 1092 189 | "Records: %ld Duplicates: %ld Warnings: %ld", 190 | #define ER_INSERT_TABLE_USED 1093 191 | "INSERT TABLE '%-.64s' isn't allowed in FROM table list", 192 | #define ER_NO_SUCH_THREAD 1094 193 | "Unknown thread id: %lu", 194 | #define ER_KILL_DENIED_ERROR 1095 195 | "You are not owner of thread %lu", 196 | #define ER_NO_TABLES_USED 1096 197 | "No tables used", 198 | #define ER_TOO_BIG_SET 1097 199 | "Too many strings for column %-.64s and SET", 200 | #define ER_NO_UNIQUE_LOGFILE 1098 201 | "Can't generate a unique log-filename %-.64s.(1-999)\n", 202 | #define ER_TABLE_NOT_LOCKED_FOR_WRITE 1099 203 | "Table '%-.64s' was locked with a READ lock and can't be updated", 204 | #define ER_TABLE_NOT_LOCKED 1100 205 | "Table '%-.64s' was not locked with LOCK TABLES", 206 | #define ER_BLOB_CANT_HAVE_DEFAULT 1101 207 | "BLOB column '%-.64s' can't have a default value", 208 | #define ER_WRONG_DB_NAME 1102 209 | "Incorrect database name '%-.100s'", 210 | #define ER_WRONG_TABLE_NAME 1103 211 | "Incorrect table name '%-.100s'", 212 | #define ER_TOO_BIG_SELECT 1104 213 | "The SELECT would examine too many records and probably take a very long time. Check your WHERE and use SET OPTION SQL_BIG_SELECTS=1 if the SELECT is ok", 214 | #define ER_UNKNOWN_ERROR 1105 215 | "Unknown error", 216 | #define ER_UNKNOWN_PROCEDURE 1106 217 | "Unknown procedure '%-.64s'", 218 | #define ER_WRONG_PARAMCOUNT_TO_PROCEDURE 1107 219 | "Incorrect parameter count to procedure '%-.64s'", 220 | #define ER_WRONG_PARAMETERS_TO_PROCEDURE 1108 221 | "Incorrect parameters to procedure '%-.64s'", 222 | #define ER_UNKNOWN_TABLE 1109 223 | "Unknown table '%-.64s' in %-.32s", 224 | #define ER_FIELD_SPECIFIED_TWICE 1110 225 | "Column '%-.64s' specified twice", 226 | #define ER_INVALID_GROUP_FUNC_USE 1111 227 | "Invalid use of group function", 228 | #define ER_UNSUPPORTED_EXTENSION 1112 229 | "Table '%-.64s' uses an extension that doesn't exist in this MySQL version", 230 | #define ER_TABLE_MUST_HAVE_COLUMNS 1113 231 | "A table must have at least 1 column", 232 | #define ER_RECORD_FILE_FULL 1114 233 | "The table '%-.64s' is full", 234 | #define ER_UNKNOWN_CHARACTER_SET 1115 235 | "Unknown character set: '%-.64s'", 236 | #define ER_TOO_MANY_TABLES 1116 237 | "Too many tables. MySQL can only use %d tables in a join", 238 | #define ER_TOO_MANY_FIELDS 1117 239 | "Too many columns", 240 | #define ER_TOO_BIG_ROWSIZE 1118 241 | "Too big row size. The maximum row size, not counting BLOBs, is %d. You have to change some fields to BLOBs", 242 | #define ER_STACK_OVERRUN 1119 243 | "Thread stack overrun: Used: %ld of a %ld stack. Use 'mysqld -O thread_stack=#' to specify a bigger stack if needed", 244 | #define ER_WRONG_OUTER_JOIN 1120 245 | "Cross dependency found in OUTER JOIN. Examine your ON conditions", 246 | #define ER_NULL_COLUMN_IN_INDEX 1121 247 | "Column '%-.64s' is used with UNIQUE or INDEX but is not defined as NOT NULL", 248 | #define ER_CANT_FIND_UDF 1122 249 | "Can't load function '%-.64s'", 250 | #define ER_CANT_INITIALIZE_UDF 1123 251 | "Can't initialize function '%-.64s'; %-.80s", 252 | #define ER_UDF_NO_PATHS 1124 253 | "No paths allowed for shared library", 254 | #define ER_UDF_EXISTS 1125 255 | "Function '%-.64s' already exist", 256 | #define ER_CANT_OPEN_LIBRARY 1126 257 | "Can't open shared library '%-.64s' (errno: %d %-.64s)", 258 | #define ER_CANT_FIND_DL_ENTRY 1127 259 | "Can't find function '%-.64s' in library'", 260 | #define ER_FUNCTION_NOT_DEFINED 1128 261 | "Function '%-.64s' is not defined", 262 | #define ER_HOST_IS_BLOCKED 1129 263 | "Host '%-.64s' is blocked because of many connection errors. Unblock with 'mysqladmin flush-hosts'", 264 | #define ER_HOST_NOT_PRIVILEGED 1130 265 | "Host '%-.64s' is not allowed to connect to this MySQL server", 266 | #define ER_PASSWORD_ANONYMOUS_USER 1131 267 | "You are using MySQL as an anonymous users and anonymous users are not allowed to change passwords", 268 | #define ER_PASSWORD_NOT_ALLOWED 1132 269 | "You must have privileges to update tables in the mysql database to be able to change passwords for others", 270 | #define ER_PASSWORD_NO_MATCH 1133 271 | "Can't find any matching row in the user table", 272 | #define ER_UPDATE_INFO 1134 273 | "Rows matched: %ld Changed: %ld Warnings: %ld", 274 | #define ER_CANT_CREATE_THREAD 1135 275 | "Can't create a new thread (errno %d). If you are not out of available memory, you can consult the manual for a possible OS-dependent bug", 276 | #define ER_WRONG_VALUE_COUNT_ON_ROW 1136 277 | "Column count doesn't match value count at row %ld", 278 | #define ER_CANT_REOPEN_TABLE 1137 279 | "Can't reopen table: '%-.64s'", 280 | #define ER_INVALID_USE_OF_NULL 1138 281 | "Invalid use of NULL value", 282 | #define ER_REGEXP_ERROR 1139 283 | "Got error '%-.64s' from regexp", 284 | #define ER_MIX_OF_GROUP_FUNC_AND_FIELDS 1140 285 | "Mixing of GROUP columns (MIN(),MAX(),COUNT()...) with no GROUP columns is illegal if there is no GROUP BY clause", 286 | #define ER_NONEXISTING_GRANT 1141 287 | "There is no such grant defined for user '%-.32s' on host '%-.64s'", 288 | #define ER_TABLEACCESS_DENIED_ERROR 1142 289 | "%-.16s command denied to user: '%-.32s@%-.64s' for table '%-.64s'", 290 | #define ER_COLUMNACCESS_DENIED_ERROR 1143 291 | "%-.16s command denied to user: '%-.32s@%-.64s' for column '%-.64s' in table '%-.64s'", 292 | #define ER_ILLEGAL_GRANT_FOR_TABLE 1144 293 | "Illegal GRANT/REVOKE command. Please consult the manual which privileges can be used.", 294 | #define ER_GRANT_WRONG_HOST_OR_USER 1145 295 | "The host or user argument to GRANT is too long", 296 | #define ER_NO_SUCH_TABLE 1146 297 | "Table '%-.64s.%-.64s' doesn't exist", 298 | #define ER_NONEXISTING_TABLE_GRANT 1147 299 | "There is no such grant defined for user '%-.32s' on host '%-.64s' on table '%-.64s'", 300 | #define ER_NOT_ALLOWED_COMMAND 1148 301 | "The used command is not allowed with this MySQL version", 302 | #define ER_SYNTAX_ERROR 1149 303 | "You have an error in your SQL syntax", 304 | #define ER_DELAYED_CANT_CHANGE_LOCK 1150 305 | "Delayed insert thread couldn't get requested lock for table %-.64s", 306 | #define ER_TOO_MANY_DELAYED_THREADS 1151 307 | "Too many delayed threads in use", 308 | #define ER_ABORTING_CONNECTION 1152 309 | "Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)", 310 | #define ER_NET_PACKET_TOO_LARGE 1153 311 | "Got a packet bigger than 'max_allowed_packet'", 312 | #define ER_NET_READ_ERROR_FROM_PIPE 1154 313 | "Got a read error from the connection pipe", 314 | #define ER_NET_FCNTL_ERROR 1155 315 | "Got an error from fcntl()", 316 | #define ER_NET_PACKETS_OUT_OF_ORDER 1156 317 | "Got packets out of order", 318 | #define ER_NET_UNCOMPRESS_ERROR 1157 319 | "Couldn't uncompress communication packet", 320 | #define ER_NET_READ_ERROR 1158 321 | "Got an error reading communication packets", 322 | #define ER_NET_READ_INTERRUPTED 1159 323 | "Got timeout reading communication packets", 324 | #define ER_NET_ERROR_ON_WRITE 1160 325 | "Got an error writing communication packets", 326 | #define ER_NET_WRITE_INTERRUPTED 1161 327 | "Got timeout writing communication packets", 328 | #define ER_TOO_LONG_STRING 1162 329 | "Result string is longer than max_allowed_packet", 330 | #define ER_TABLE_CANT_HANDLE_BLOB 1163 331 | "The used table type doesn't support BLOB/TEXT columns", 332 | #define ER_TABLE_CANT_HANDLE_AUTO_INCREMENT 1164 333 | "The used table type doesn't support AUTO_INCREMENT columns", 334 | #define ER_DELAYED_INSERT_TABLE_LOCKED 1165 335 | "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES", 336 | #define ER_WRONG_COLUMN_NAME 1166 337 | "Incorrect column name '%-.100s'", 338 | #define ER_WRONG_KEY_COLUMN 1167 339 | "The used table handler can't index column '%-.64s'", 340 | #define ER_WRONG_MRG_TABLE 1168 341 | "All tables in the MERGE table are not identically defined", 342 | #define ER_DUP_UNIQUE 1169 343 | "Can't write, because of unique constraint, to table '%-.64s'", 344 | #define ER_BLOB_KEY_WITHOUT_LENGTH 1170 345 | "BLOB column '%-.64s' used in key specification without a key length", 346 | #define ER_PRIMARY_CANT_HAVE_NULL 1171 347 | "All parts of a PRIMARY KEY must be NOT NULL; If you need NULL in a key, use UNIQUE instead", 348 | #define ER_TOO_MANY_ROWS 1172 349 | "Result consisted of more than one row", 350 | #define ER_REQUIRES_PRIMARY_KEY 1173 351 | "This table type requires a primary key", 352 | #define ER_NO_RAID_COMPILED 1174 353 | "This version of MySQL is not compiled with RAID support", 354 | #define ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE 1175 355 | "You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column", 356 | -------------------------------------------------------------------------------- /mysql.mli: -------------------------------------------------------------------------------- 1 | (* 2 | Ocaml-MySQL: MySQL bindings for Ocaml. 3 | Copyright (C) 2001-2003 Shawn Wagner 4 | 1998, 1999 Christian Lindig 5 | 6 | This library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Lesser General Public 8 | License as published by the Free Software Foundation; either 9 | version 2.1 of the License, or (at your option) any later version. 10 | 11 | This library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | *) 20 | 21 | (** 22 | This module provides access to MySQL databases, roughly following the C API 23 | *) 24 | 25 | (** {1 Database connections} *) 26 | 27 | (** {2 Opening a connection} *) 28 | 29 | (** database connection handle *) 30 | type dbd 31 | 32 | (** Login information for a database. Use [None] for default values *) 33 | type db = { 34 | dbhost : string option; (** database server host *) 35 | dbname : string option; (** database name *) 36 | dbport : int option; (** port *) 37 | dbpwd : string option; (** user password *) 38 | dbuser : string option; (** database user *) 39 | dbsocket : string option; (** unix socket path *) 40 | } 41 | 42 | (** Login information using all defaults *) 43 | val defaults: db 44 | 45 | type protocol = 46 | | PROTOCOL_DEFAULT 47 | | PROTOCOL_TCP 48 | | PROTOCOL_SOCKET 49 | | PROTOCOL_PIPE 50 | | PROTOCOL_MEMORY 51 | 52 | type db_option = 53 | | OPT_COMPRESS 54 | | OPT_NAMED_PIPE 55 | | OPT_LOCAL_INFILE of bool 56 | | OPT_RECONNECT of bool 57 | | OPT_SSL_VERIFY_SERVER_CERT of bool 58 | | REPORT_DATA_TRUNCATION of bool 59 | | SECURE_AUTH of bool 60 | | OPT_PROTOCOL of protocol 61 | | OPT_CONNECT_TIMEOUT of int (** Connect timeout in seconds *) 62 | | OPT_READ_TIMEOUT of int (** The timeout in seconds for attempts to read from the server. 63 | Each attempt uses this timeout value and there are retries if 64 | necessary, so the total effective timeout value is three times 65 | the option value. *) 66 | | OPT_WRITE_TIMEOUT of int (** The timeout in seconds for attempts to write to the server. 67 | Each attempt uses this timeout value and there are net_retry_count 68 | retries if necessary, so the total effective timeout value is 69 | net_retry_count times the option value. *) 70 | | INIT_COMMAND of string 71 | | READ_DEFAULT_FILE of string (** Read options from the named option file instead of from my.cnf. *) 72 | | READ_DEFAULT_GROUP of string (** Read options from the named group *) 73 | | SET_CHARSET_DIR of string (** The path name to the directory that contains character set definition files. *) 74 | | SET_CHARSET_NAME of string (** The name of the character set to use as the default character set. *) 75 | | SHARED_MEMORY_BASE_NAME of string (** The name of the shared-memory object for communication to the server 76 | on Windows, if the server supports shared-memory connections *) 77 | | OPT_FOUND_ROWS (** Return the number of found (matched) rows, not the number of changed rows. *) 78 | 79 | (** 80 | Initialize library (in particular initializes default character set for {!escape} NB it is recommended to always use {!real_escape}) 81 | NB init is called automatically when db handle is created 82 | *) 83 | val init : unit -> unit 84 | 85 | (** [connect ?options db] connects to the database [db] and returns a handle for further use 86 | @param options connection specific options, default empty list 87 | *) 88 | val connect : ?options:db_option list -> db -> dbd 89 | 90 | (** Shortcut for connecting to a database with mostly default field values *) 91 | val quick_connect: ?options:db_option list -> ?host:string -> ?database:string -> ?port:int -> ?password:string -> ?user:string -> ?socket:string -> unit -> dbd 92 | 93 | (** {2 Altering a connection} *) 94 | 95 | (** [set_charset dbd charset] sets the current character set for [dbd] (aka [SET NAMES]). 96 | It is strongly recommended to set the charset explicitly after connecting to database, using this function. 97 | Available character sets are stored in [INFORMATION_SCHEMA.CHARACTER_SETS] table ([SHOW CHARACTER SET]). 98 | *) 99 | val set_charset : dbd -> string -> unit 100 | 101 | (** [change_user dbd db] tries to change the current user and database. 102 | The host and port fields of db are ignored. *) 103 | val change_user : dbd -> db -> unit 104 | 105 | (** Another shortcut *) 106 | val quick_change: ?user:string -> ?password:string -> ?database:string -> dbd -> unit 107 | 108 | (** [select_db] switches to a new db, using the current user and password. *) 109 | val select_db : dbd -> string -> unit 110 | 111 | (** [disconnect dbd] releases a database connection [dbd]. The handle [dbd] becomes invalid *) 112 | val disconnect : dbd -> unit 113 | 114 | (** [ping dbd] makes sure the connection to the server is up, and re-establishes it if needed. *) 115 | val ping : dbd -> unit 116 | 117 | (** {2 Information about a connection} *) 118 | 119 | (** [list_db] Return a list of all visible databases on the current server *) 120 | val list_dbs : dbd -> ?pat:string -> unit -> string array option 121 | 122 | (** Return the MySQL client library version *) 123 | val client_info : unit -> string 124 | 125 | (** Return information about the server connection *) 126 | val host_info : dbd -> string 127 | 128 | (** Return the MySQL server version *) 129 | val server_info : dbd -> string 130 | 131 | (** Return the protocol version being used *) 132 | val proto_info : dbd -> int 133 | 134 | (** {2 Errors} *) 135 | 136 | (** When most of the API functions fail, they raise this exception with a description of the failure. *) 137 | exception Error of string 138 | 139 | (** Possible error codes from a failed operation that doesn't throw an exception *) 140 | (* Auto-generated on Thu Feb 20 06:10:26 2003 from MySQL headers. *) 141 | type error_code = Aborting_connection | Access_denied_error | Alter_info | Bad_db_error | Bad_field_error | Bad_host_error | Bad_null_error | Bad_table_error | Blob_cant_have_default | Blob_key_without_length | Blob_used_as_key | Blobs_and_no_terminated | Cant_create_db | Cant_create_file | Cant_create_table | Cant_create_thread | Cant_delete_file | Cant_drop_field_or_key | Cant_find_dl_entry | Cant_find_system_rec | Cant_find_udf | Cant_get_stat | Cant_get_wd | Cant_initialize_udf | Cant_lock | Cant_open_file | Cant_open_library | Cant_read_charset | Cant_read_dir | Cant_remove_all_fields | Cant_reopen_table | Cant_set_wd | Checkread | Columnaccess_denied_error | Commands_out_of_sync | Con_count_error | Conn_host_error | Connection_error | Db_create_exists | Db_drop_delete | Db_drop_exists | Db_drop_rmdir | Dbaccess_denied_error | Delayed_cant_change_lock | Delayed_insert_table_locked | Disk_full | Dup_entry | Dup_fieldname | Dup_key | Dup_keyname | Dup_unique | Empty_query | Error_on_close | Error_on_read | Error_on_rename | Error_on_write | Field_specified_twice | File_exists_error | File_not_found | File_used | Filsort_abort | Forcing_close | Form_not_found | Function_not_defined | Get_errno | Got_signal | Grant_wrong_host_or_user | Handshake_error | Hashchk | Host_is_blocked | Host_not_privileged | Illegal_grant_for_table | Illegal_ha | Insert_info | Insert_table_used | Invalid_default | Invalid_group_func_use | Invalid_use_of_null | Ipsock_error | Key_column_does_not_exits | Key_not_found | Kill_denied_error | Load_info | Localhost_connection | Mix_of_group_func_and_fields | Multiple_pri_key | Namedpipe_connection | Namedpipeopen_error | Namedpipesetstate_error | Namedpipewait_error | Net_error_on_write | Net_fcntl_error | Net_packet_too_large | Net_packets_out_of_order | Net_read_error | Net_read_error_from_pipe | Net_read_interrupted | Net_uncompress_error | Net_write_interrupted | Nisamchk | No | No_db_error | No_raid_compiled | No_such_index | No_such_table | No_such_thread | No_tables_used | No_unique_logfile | Non_uniq_error | Nonexisting_grant | Nonexisting_table_grant | Nonuniq_table | Normal_shutdown | Not_allowed_command | Not_form_file | Not_keyfile | Null_column_in_index | Old_keyfile | Open_as_readonly | Out_of_memory | Out_of_resources | Out_of_sortmemory | Outofmemory | Parse_error | Password_anonymous_user | Password_no_match | Password_not_allowed | Primary_cant_have_null | Ready | Record_file_full | Regexp_error | Requires_primary_key | Server_gone_error | Server_handshake_err | Server_lost | Server_shutdown | Shutdown_complete | Socket_create_error | Stack_overrun | Syntax_error | Table_cant_handle_auto_increment | Table_cant_handle_blob | Table_exists_error | Table_must_have_columns | Table_not_locked | Table_not_locked_for_write | Tableaccess_denied_error | Tcp_connection | Textfile_not_readable | Too_big_fieldlength | Too_big_rowsize | Too_big_select | Too_big_set | Too_long_ident | Too_long_key | Too_long_string | Too_many_delayed_threads | Too_many_fields | Too_many_key_parts | Too_many_keys | Too_many_rows | Too_many_tables | Udf_exists | Udf_no_paths | Unexpected_eof | Unknown_character_set | Unknown_com_error | Unknown_error | Unknown_host | Unknown_procedure | Unknown_table | Unsupported_extension | Update_info | Update_without_key_in_safe_mode | Version_error | Wrong_auto_key | Wrong_column_name | Wrong_db_name | Wrong_field_spec | Wrong_field_terminators | Wrong_field_with_group | Wrong_group_field | Wrong_host_info | Wrong_key_column | Wrong_mrg_table | Wrong_outer_join | Wrong_paramcount_to_procedure | Wrong_parameters_to_procedure | Wrong_sub_key | Wrong_sum_select | Wrong_table_name | Wrong_value_count | Wrong_value_count_on_row | Yes 142 | 143 | (** The status of a query *) 144 | type status = 145 | | StatusOK (** The query was successful *) 146 | | StatusEmpty (** The query was successful, but found no results *) 147 | | StatusError of error_code (** There was some problem with the query *) 148 | 149 | (** [status dbd] returns the status of the last action on [dbd] *) 150 | val status : dbd -> status 151 | 152 | (** [errno dbd] returns the error_code for the last action on [dbd]. Useful when 153 | you know there's an error, to avoid an extra layer of matching in [status] *) 154 | val errno : dbd -> error_code 155 | 156 | (** [errmsg dbd] returns an error message in case the last operation on [dbd] 157 | failed *) 158 | val errmsg : dbd -> string option 159 | 160 | (** {1 Queries} *) 161 | 162 | (** {2 Making a query} *) 163 | 164 | (** handle to access the result of a query *) 165 | type result 166 | 167 | (** [exec dbd str] executes a SQL statement and returns a handle to obtain 168 | the result. Check [status] for errors! *) 169 | val exec : dbd -> string -> result 170 | 171 | (** {2 Getting the results of a query} *) 172 | 173 | (** [fetch result] returns the next row from a result as [Some a] or [None] 174 | in case there is no next result. The array [a] contains the values from 175 | the current row, where NULL values are denoted by [None]. Use 176 | [column] to fetch single values by field name instead of by 177 | position *) 178 | val fetch : result -> string option array option 179 | 180 | (** [to_row result row] sets the current row. 181 | 182 | @raise Invalid_argument if the row is out of range. 183 | *) 184 | val to_row : result -> int64 -> unit 185 | 186 | (** [size result] returns the size of the actual result set (number of 187 | rows) *) 188 | val size : result -> int64 189 | 190 | (** [iter result f] applies f to each row of result in turn, starting 191 | from the first. iter_col applies f to the value of the named column 192 | in every row. 193 | 194 | The iter versions return unit, the map versions return a list of 195 | the results of all the function applications. If there were no rows 196 | in the result, returns an empty list. 197 | 198 | The iter forms are all tail-recursive, so they can be used with any 199 | size of results. The map forms are tail-recursive, but take up 200 | space with the list they build. *) 201 | 202 | val iter : result -> f:(string option array -> unit) -> unit 203 | val iter_col : result -> key:string -> f:(string option -> unit) -> unit 204 | val iter_cols : result -> key:string array -> f:(string option array -> unit) -> unit 205 | val map : result -> f:(string option array -> 'a) -> 'a list 206 | val map_col : result -> key:string -> f:(string option -> 'a) -> 'a list 207 | val map_cols : result -> key:string array -> f:(string option array -> 'a) -> 'a list 208 | 209 | (** Returns one field of a result row based on column name. *) 210 | val column : result -> key:string -> row:string option array -> string option 211 | 212 | (** {2 Metainformation about a result set} *) 213 | 214 | (** The type of a database field. Each of these represents one or more MySQL data types. *) 215 | type dbty = 216 | | IntTy 217 | | FloatTy 218 | | StringTy 219 | | SetTy 220 | | EnumTy 221 | | DateTimeTy 222 | | DateTy 223 | | TimeTy 224 | | YearTy 225 | | TimeStampTy 226 | | UnknownTy 227 | | Int64Ty 228 | | BlobTy 229 | | DecimalTy 230 | 231 | (** The type that describes a field of a table or result *) 232 | type field = { name : string; (** Name of the field *) 233 | table : string option; (** Table name, or None if a constructed field *) 234 | def : string option; (** Default value of the field *) 235 | ty : dbty (** The type of data stored in the field *); 236 | max_length : int; (** Maximum width of field for the result set *) 237 | flags : int; (** Flags set *) 238 | decimals : int (** Number of decimals for numeric fields *) 239 | } 240 | 241 | (** Turn a field-type type into a string for printing *) 242 | val pretty_type: dbty -> string 243 | 244 | (** [affected result] returns the number of rows changed by the last 245 | UPDATE, or deleted by the last DELETE, or added by the last INSERT, 246 | or the number of rows returned by the last SELECT *) 247 | val affected : dbd -> int64 248 | 249 | (** [insert_id result] returns the ID generated by the last INSERT 250 | query in a table with an AUTO_INCREMENT column. See the MySQL 251 | documentation for caveats. *) 252 | val insert_id: dbd -> int64 253 | 254 | (** [fields result] returns the number of fields in a row *) 255 | val fields : result -> int 256 | 257 | (** [names result] returns an array of the field names for the current result *) 258 | val names : result -> string array 259 | 260 | (** [types result] returns an array with the MySQL types of the current result *) 261 | val types : result -> dbty array 262 | 263 | (** Returns the information on the next field *) 264 | val fetch_field : result -> field option 265 | 266 | (** Returns information on all the fields *) 267 | val fetch_fields : result -> field array option 268 | 269 | (** Returns information on a specific field, with the first field numbered 0 *) 270 | val fetch_field_dir : result -> int -> field option 271 | 272 | (** {1 Working with MySQL data types} *) 273 | 274 | (** [escape str] returns the same string as [str] in MySQL syntax with 275 | special characters quoted to not confuse the MySQL parser. 276 | 277 | @deprecated This function poses a security risk (doesn't take into consideration 278 | the character set of the current connection and may allow SQL injection pass through). 279 | Use {!real_escape} instead. 280 | *) 281 | val escape : string -> string 282 | 283 | (** [real_escape dbd str] returns [str] encoded 284 | to an escaped SQL string according to the current character set of [dbd] *) 285 | val real_escape : dbd -> string -> string 286 | 287 | (** [xxx2ml str] decodes a MySQL value of type xxx into a corresponding OCaml value *) 288 | 289 | (** Use for all MySQL signed integer types but BIGINT *) 290 | val int2ml : string -> int 291 | val decimal2ml : string -> string 292 | val int322ml : string -> int32 293 | val nativeint2ml : string -> nativeint 294 | 295 | (* Use for MySQL signed BIGINT type *) 296 | val int642ml : string -> int64 297 | 298 | (** Use for MySQL FLOAT, DOUBLE and REAL types *) 299 | val float2ml : string -> float 300 | 301 | (** Use for MySQL CHAR and VARCHAR types *) 302 | val str2ml : string -> string 303 | 304 | val enum2ml : string -> string 305 | 306 | (** Use for all MySQL BLOB and TEXT types *) 307 | val blob2ml : string -> string 308 | 309 | val set2ml : string -> string list 310 | val datetime2ml : string -> int * int * int * int * int * int 311 | val date2ml : string -> int * int * int 312 | val time2ml : string -> int * int * int 313 | val year2ml : string -> int 314 | val timestamp2ml : string -> int * int * int * int * int * int 315 | 316 | (** Not yet supported: DECIMAL and NUMERIC *) 317 | 318 | (** [opt f v] applies [f] to optional value [v]. Use this to fetch 319 | data of known type from database fields which might be NULL: 320 | [opt int2ml str] *) 321 | val opt : ('a -> 'b) -> 'a option -> 'b option 322 | 323 | 324 | (** [not_null f v] applies [f] to [Some v]. Use this to fetch data of known 325 | type from database fields which never can be NULL: [not_null int2ml str] 326 | *) 327 | val not_null : ('a -> 'b) -> 'a option -> 'b 328 | 329 | (** 330 | [ml2xxx v] encodes [v] into MySQL syntax. 331 | [ml2rxxx v] encodes [v] into MySQL syntax using [real_escape]. 332 | *) 333 | 334 | (** @deprecated This function uses {!escape} which poses a security risk. Use {!ml2rstr} instead. *) 335 | val ml2str : string -> string 336 | val ml2rstr : dbd -> string -> string 337 | 338 | (** @deprecated This function uses {!escape} which poses a security risk. Use {!ml2rblob} instead. *) 339 | val ml2blob : string -> string 340 | val ml2rblob : dbd -> string -> string 341 | val ml2int : int -> string 342 | val ml2decimal : string -> string 343 | val ml322int : int32 -> string 344 | val ml642int : int64 -> string 345 | val ml2float : float -> string 346 | val ml2enum : string -> string 347 | val ml2renum : dbd -> string -> string 348 | val ml2set : string list -> string 349 | val ml2rset : dbd -> string list -> string 350 | val ml2datetime : int * int * int * int * int * int -> string 351 | val ml2datetimel : year:int -> month:int -> day:int -> hour:int -> min:int -> sec:int -> string 352 | val ml2date : int * int * int -> string 353 | val ml2datel : year:int -> month:int -> day:int -> string 354 | val ml2time : int * int * int -> string 355 | val ml2timel : hour:int -> min:int -> sec:int -> string 356 | val ml2year : int -> string 357 | val ml2timestamp : int * int * int * int * int * int -> string 358 | val ml2timestampl : year:int -> month:int -> day:int -> hour:int -> min:int -> sec:int -> string 359 | 360 | 361 | (** [values vs] takes a list of strings and returns a string 362 | "(a,b,c ..)" where values are separated by comma and the whole 363 | list is enclosed into parentheses. This is useful to construct 364 | SQL `insert ... values ( .. )' statements *) 365 | val values : string list -> string 366 | 367 | (** {1 Prepared statements} *) 368 | 369 | (** Prepared statements with parameters. Consult the MySQL manual for detailed description 370 | and possible problems. *) 371 | module Prepared : sig 372 | 373 | (** Prepared statement *) 374 | type stmt 375 | 376 | (** Prepared query result (rowset) *) 377 | type stmt_result 378 | 379 | (** Create prepared statement. Placeholders for parameters are [?] and [\@param]. 380 | Returned prepared statement is only valid in the context of this connection and 381 | can be reused many times during the lifetime of the connection. *) 382 | val create : dbd -> string -> stmt 383 | 384 | (** Execute the prepared statement with the specified values for parameters. *) 385 | val execute : stmt -> string array -> stmt_result 386 | 387 | (** Same as {!execute}, but with support for NULL values. *) 388 | val execute_null : stmt -> string option array -> stmt_result 389 | 390 | (** @return Number of rows affected by the last execution of this statement. *) 391 | val affected : stmt -> int64 392 | 393 | (** @return The rowid of the last inserted row. *) 394 | val insert_id : stmt -> int64 395 | 396 | (** @return the error code for the last operation on this statement, [0] for success. *) 397 | val real_status : stmt -> int 398 | 399 | (** @return the next row of the result set. *) 400 | val fetch : stmt_result -> string option array option 401 | 402 | (** @return metadata on the statement's result set. *) 403 | val result_metadata : stmt -> result 404 | 405 | (** Destroy the prepared statement *) 406 | val close : stmt -> unit 407 | 408 | end 409 | -------------------------------------------------------------------------------- /mysql.ml: -------------------------------------------------------------------------------- 1 | (* 2 | Ocaml-MySQL: MySQL bindings for Ocaml. 3 | Copyright (C) 2001-2003 Shawn Wagner 4 | 1998, 1999 Christian Lindig 5 | 6 | This library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Lesser General Public 8 | License as published by the Free Software Foundation; either 9 | version 2.1 of the License, or (at your option) any later version. 10 | 11 | This library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | *) 20 | 21 | module Map = MoreLabels.Map 22 | module String = StdLabels.String 23 | module Array = StdLabels.Array 24 | 25 | module StrMap = Map.Make(struct type t = string let compare = compare end) 26 | 27 | exception Error of string 28 | 29 | let _ = Callback.register_exception "mysql error" (Error "Registering Callback") 30 | 31 | (* let error msg = raise (Error msg) *) 32 | let fail msg = raise (Failure msg) 33 | 34 | type dbd (* database connection handle *) 35 | type result (* handle to access result from query *) 36 | 37 | external init : unit -> unit = "db_library_init" 38 | 39 | (* Do not change any type definition that is used by external functions 40 | without changing the C source code accordingly! *) 41 | 42 | (* Error codes *) 43 | (* Auto-generated on Thu Feb 20 06:10:26 2003 from MySQL headers. *) 44 | type error_code = Aborting_connection | Access_denied_error | Alter_info | Bad_db_error | Bad_field_error | Bad_host_error | Bad_null_error | Bad_table_error | Blob_cant_have_default | Blob_key_without_length | Blob_used_as_key | Blobs_and_no_terminated | Cant_create_db | Cant_create_file | Cant_create_table | Cant_create_thread | Cant_delete_file | Cant_drop_field_or_key | Cant_find_dl_entry | Cant_find_system_rec | Cant_find_udf | Cant_get_stat | Cant_get_wd | Cant_initialize_udf | Cant_lock | Cant_open_file | Cant_open_library | Cant_read_charset | Cant_read_dir | Cant_remove_all_fields | Cant_reopen_table | Cant_set_wd | Checkread | Columnaccess_denied_error | Commands_out_of_sync | Con_count_error | Conn_host_error | Connection_error | Db_create_exists | Db_drop_delete | Db_drop_exists | Db_drop_rmdir | Dbaccess_denied_error | Delayed_cant_change_lock | Delayed_insert_table_locked | Disk_full | Dup_entry | Dup_fieldname | Dup_key | Dup_keyname | Dup_unique | Empty_query | Error_on_close | Error_on_read | Error_on_rename | Error_on_write | Field_specified_twice | File_exists_error | File_not_found | File_used | Filsort_abort | Forcing_close | Form_not_found | Function_not_defined | Get_errno | Got_signal | Grant_wrong_host_or_user | Handshake_error | Hashchk | Host_is_blocked | Host_not_privileged | Illegal_grant_for_table | Illegal_ha | Insert_info | Insert_table_used | Invalid_default | Invalid_group_func_use | Invalid_use_of_null | Ipsock_error | Key_column_does_not_exits | Key_not_found | Kill_denied_error | Load_info | Localhost_connection | Mix_of_group_func_and_fields | Multiple_pri_key | Namedpipe_connection | Namedpipeopen_error | Namedpipesetstate_error | Namedpipewait_error | Net_error_on_write | Net_fcntl_error | Net_packet_too_large | Net_packets_out_of_order | Net_read_error | Net_read_error_from_pipe | Net_read_interrupted | Net_uncompress_error | Net_write_interrupted | Nisamchk | No | No_db_error | No_raid_compiled | No_such_index | No_such_table | No_such_thread | No_tables_used | No_unique_logfile | Non_uniq_error | Nonexisting_grant | Nonexisting_table_grant | Nonuniq_table | Normal_shutdown | Not_allowed_command | Not_form_file | Not_keyfile | Null_column_in_index | Old_keyfile | Open_as_readonly | Out_of_memory | Out_of_resources | Out_of_sortmemory | Outofmemory | Parse_error | Password_anonymous_user | Password_no_match | Password_not_allowed | Primary_cant_have_null | Ready | Record_file_full | Regexp_error | Requires_primary_key | Server_gone_error | Server_handshake_err | Server_lost | Server_shutdown | Shutdown_complete | Socket_create_error | Stack_overrun | Syntax_error | Table_cant_handle_auto_increment | Table_cant_handle_blob | Table_exists_error | Table_must_have_columns | Table_not_locked | Table_not_locked_for_write | Tableaccess_denied_error | Tcp_connection | Textfile_not_readable | Too_big_fieldlength | Too_big_rowsize | Too_big_select | Too_big_set | Too_long_ident | Too_long_key | Too_long_string | Too_many_delayed_threads | Too_many_fields | Too_many_key_parts | Too_many_keys | Too_many_rows | Too_many_tables | Udf_exists | Udf_no_paths | Unexpected_eof | Unknown_character_set | Unknown_com_error | Unknown_error | Unknown_host | Unknown_procedure | Unknown_table | Unsupported_extension | Update_info | Update_without_key_in_safe_mode | Version_error | Wrong_auto_key | Wrong_column_name | Wrong_db_name | Wrong_field_spec | Wrong_field_terminators | Wrong_field_with_group | Wrong_group_field | Wrong_host_info | Wrong_key_column | Wrong_mrg_table | Wrong_outer_join | Wrong_paramcount_to_procedure | Wrong_parameters_to_procedure | Wrong_sub_key | Wrong_sum_select | Wrong_table_name | Wrong_value_count | Wrong_value_count_on_row | Yes 45 | 46 | let error_of_int code = match code with 47 | | 1000 -> Hashchk 48 | | 1001 -> Nisamchk 49 | | 1002 -> No 50 | | 1003 -> Yes 51 | | 1004 -> Cant_create_file 52 | | 1005 -> Cant_create_table 53 | | 1006 -> Cant_create_db 54 | | 1007 -> Db_create_exists 55 | | 1008 -> Db_drop_exists 56 | | 1009 -> Db_drop_delete 57 | | 1010 -> Db_drop_rmdir 58 | | 1011 -> Cant_delete_file 59 | | 1012 -> Cant_find_system_rec 60 | | 1013 -> Cant_get_stat 61 | | 1014 -> Cant_get_wd 62 | | 1015 -> Cant_lock 63 | | 1016 -> Cant_open_file 64 | | 1017 -> File_not_found 65 | | 1018 -> Cant_read_dir 66 | | 1019 -> Cant_set_wd 67 | | 1020 -> Checkread 68 | | 1021 -> Disk_full 69 | | 1022 -> Dup_key 70 | | 1023 -> Error_on_close 71 | | 1024 -> Error_on_read 72 | | 1025 -> Error_on_rename 73 | | 1026 -> Error_on_write 74 | | 1027 -> File_used 75 | | 1028 -> Filsort_abort 76 | | 1029 -> Form_not_found 77 | | 1030 -> Get_errno 78 | | 1031 -> Illegal_ha 79 | | 1032 -> Key_not_found 80 | | 1033 -> Not_form_file 81 | | 1034 -> Not_keyfile 82 | | 1035 -> Old_keyfile 83 | | 1036 -> Open_as_readonly 84 | | 1037 -> Outofmemory 85 | | 1038 -> Out_of_sortmemory 86 | | 1039 -> Unexpected_eof 87 | | 1040 -> Con_count_error 88 | | 1041 -> Out_of_resources 89 | | 1042 -> Bad_host_error 90 | | 1043 -> Handshake_error 91 | | 1044 -> Dbaccess_denied_error 92 | | 1045 -> Access_denied_error 93 | | 1046 -> No_db_error 94 | | 1047 -> Unknown_com_error 95 | | 1048 -> Bad_null_error 96 | | 1049 -> Bad_db_error 97 | | 1050 -> Table_exists_error 98 | | 1051 -> Bad_table_error 99 | | 1052 -> Non_uniq_error 100 | | 1053 -> Server_shutdown 101 | | 1054 -> Bad_field_error 102 | | 1055 -> Wrong_field_with_group 103 | | 1056 -> Wrong_group_field 104 | | 1057 -> Wrong_sum_select 105 | | 1058 -> Wrong_value_count 106 | | 1059 -> Too_long_ident 107 | | 1060 -> Dup_fieldname 108 | | 1061 -> Dup_keyname 109 | | 1062 -> Dup_entry 110 | | 1063 -> Wrong_field_spec 111 | | 1064 -> Parse_error 112 | | 1065 -> Empty_query 113 | | 1066 -> Nonuniq_table 114 | | 1067 -> Invalid_default 115 | | 1068 -> Multiple_pri_key 116 | | 1069 -> Too_many_keys 117 | | 1070 -> Too_many_key_parts 118 | | 1071 -> Too_long_key 119 | | 1072 -> Key_column_does_not_exits 120 | | 1073 -> Blob_used_as_key 121 | | 1074 -> Too_big_fieldlength 122 | | 1075 -> Wrong_auto_key 123 | | 1076 -> Ready 124 | | 1077 -> Normal_shutdown 125 | | 1078 -> Got_signal 126 | | 1079 -> Shutdown_complete 127 | | 1080 -> Forcing_close 128 | | 1081 -> Ipsock_error 129 | | 1082 -> No_such_index 130 | | 1083 -> Wrong_field_terminators 131 | | 1084 -> Blobs_and_no_terminated 132 | | 1085 -> Textfile_not_readable 133 | | 1086 -> File_exists_error 134 | | 1087 -> Load_info 135 | | 1088 -> Alter_info 136 | | 1089 -> Wrong_sub_key 137 | | 1090 -> Cant_remove_all_fields 138 | | 1091 -> Cant_drop_field_or_key 139 | | 1092 -> Insert_info 140 | | 1093 -> Insert_table_used 141 | | 1094 -> No_such_thread 142 | | 1095 -> Kill_denied_error 143 | | 1096 -> No_tables_used 144 | | 1097 -> Too_big_set 145 | | 1098 -> No_unique_logfile 146 | | 1099 -> Table_not_locked_for_write 147 | | 1100 -> Table_not_locked 148 | | 1101 -> Blob_cant_have_default 149 | | 1102 -> Wrong_db_name 150 | | 1103 -> Wrong_table_name 151 | | 1104 -> Too_big_select 152 | | 1105 -> Unknown_error 153 | | 1106 -> Unknown_procedure 154 | | 1107 -> Wrong_paramcount_to_procedure 155 | | 1108 -> Wrong_parameters_to_procedure 156 | | 1109 -> Unknown_table 157 | | 1110 -> Field_specified_twice 158 | | 1111 -> Invalid_group_func_use 159 | | 1112 -> Unsupported_extension 160 | | 1113 -> Table_must_have_columns 161 | | 1114 -> Record_file_full 162 | | 1115 -> Unknown_character_set 163 | | 1116 -> Too_many_tables 164 | | 1117 -> Too_many_fields 165 | | 1118 -> Too_big_rowsize 166 | | 1119 -> Stack_overrun 167 | | 1120 -> Wrong_outer_join 168 | | 1121 -> Null_column_in_index 169 | | 1122 -> Cant_find_udf 170 | | 1123 -> Cant_initialize_udf 171 | | 1124 -> Udf_no_paths 172 | | 1125 -> Udf_exists 173 | | 1126 -> Cant_open_library 174 | | 1127 -> Cant_find_dl_entry 175 | | 1128 -> Function_not_defined 176 | | 1129 -> Host_is_blocked 177 | | 1130 -> Host_not_privileged 178 | | 1131 -> Password_anonymous_user 179 | | 1132 -> Password_not_allowed 180 | | 1133 -> Password_no_match 181 | | 1134 -> Update_info 182 | | 1135 -> Cant_create_thread 183 | | 1136 -> Wrong_value_count_on_row 184 | | 1137 -> Cant_reopen_table 185 | | 1138 -> Invalid_use_of_null 186 | | 1139 -> Regexp_error 187 | | 1140 -> Mix_of_group_func_and_fields 188 | | 1141 -> Nonexisting_grant 189 | | 1142 -> Tableaccess_denied_error 190 | | 1143 -> Columnaccess_denied_error 191 | | 1144 -> Illegal_grant_for_table 192 | | 1145 -> Grant_wrong_host_or_user 193 | | 1146 -> No_such_table 194 | | 1147 -> Nonexisting_table_grant 195 | | 1148 -> Not_allowed_command 196 | | 1149 -> Syntax_error 197 | | 1150 -> Delayed_cant_change_lock 198 | | 1151 -> Too_many_delayed_threads 199 | | 1152 -> Aborting_connection 200 | | 1153 -> Net_packet_too_large 201 | | 1154 -> Net_read_error_from_pipe 202 | | 1155 -> Net_fcntl_error 203 | | 1156 -> Net_packets_out_of_order 204 | | 1157 -> Net_uncompress_error 205 | | 1158 -> Net_read_error 206 | | 1159 -> Net_read_interrupted 207 | | 1160 -> Net_error_on_write 208 | | 1161 -> Net_write_interrupted 209 | | 1162 -> Too_long_string 210 | | 1163 -> Table_cant_handle_blob 211 | | 1164 -> Table_cant_handle_auto_increment 212 | | 1165 -> Delayed_insert_table_locked 213 | | 1166 -> Wrong_column_name 214 | | 1167 -> Wrong_key_column 215 | | 1168 -> Wrong_mrg_table 216 | | 1169 -> Dup_unique 217 | | 1170 -> Blob_key_without_length 218 | | 1171 -> Primary_cant_have_null 219 | | 1172 -> Too_many_rows 220 | | 1173 -> Requires_primary_key 221 | | 1174 -> No_raid_compiled 222 | | 1175 -> Update_without_key_in_safe_mode 223 | | 2000 -> Unknown_error 224 | | 2001 -> Socket_create_error 225 | | 2002 -> Connection_error 226 | | 2003 -> Conn_host_error 227 | | 2004 -> Ipsock_error 228 | | 2005 -> Unknown_host 229 | | 2006 -> Server_gone_error 230 | | 2007 -> Version_error 231 | | 2008 -> Out_of_memory 232 | | 2009 -> Wrong_host_info 233 | | 2010 -> Localhost_connection 234 | | 2011 -> Tcp_connection 235 | | 2012 -> Server_handshake_err 236 | | 2013 -> Server_lost 237 | | 2014 -> Commands_out_of_sync 238 | | 2015 -> Namedpipe_connection 239 | | 2016 -> Namedpipewait_error 240 | | 2017 -> Namedpipeopen_error 241 | | 2018 -> Namedpipesetstate_error 242 | | 2019 -> Cant_read_charset 243 | | 2020 -> Net_packet_too_large 244 | | _ -> Unknown_error 245 | 246 | 247 | (* Status of MySQL database after an operation. Especially indicates empty 248 | result sets *) 249 | type status = 250 | | StatusOK 251 | | StatusEmpty 252 | | StatusError of error_code 253 | 254 | (* database field type *) 255 | type dbty = 256 | | IntTy (* 0 *) 257 | | FloatTy (* 1 *) 258 | | StringTy (* 2 *) 259 | | SetTy (* 3 *) 260 | | EnumTy (* 4 *) 261 | | DateTimeTy (* 5 *) 262 | | DateTy (* 6 *) 263 | | TimeTy (* 7 *) 264 | | YearTy (* 8 *) 265 | | TimeStampTy (* 9 *) 266 | | UnknownTy (* 10 *) 267 | | Int64Ty (* 11 *) 268 | | BlobTy (* 12 *) 269 | | DecimalTy (* 13 *) 270 | 271 | let pretty_type = function 272 | | IntTy -> "integer" 273 | | FloatTy -> "float" 274 | | StringTy -> "string" 275 | | SetTy -> "set" 276 | | EnumTy -> "enum" 277 | | DateTimeTy -> "datetime" 278 | | DateTy -> "date" 279 | | TimeTy -> "time" 280 | | YearTy -> "year" 281 | | TimeStampTy -> "timestamp" 282 | | UnknownTy -> "unknown" 283 | | Int64Ty -> "int64" 284 | | BlobTy -> "blob" 285 | | DecimalTy -> "decimal" 286 | 287 | 288 | (* database login informations -- use None for default values *) 289 | 290 | type db = 291 | { dbhost : string option (* database server host *) 292 | ; dbname : string option (* database name *) 293 | ; dbport : int option (* port *) 294 | ; dbpwd : string option (* user password *) 295 | ; dbuser : string option (* database user *) 296 | ; dbsocket : string option (* unix socket path *) 297 | } 298 | 299 | type field = { name : string; (* Name of the field *) 300 | table : string option; (* Table name, or None if a constructed field *) 301 | def : string option; (* Default value of the field *) 302 | ty : dbty; 303 | max_length : int; (* Maximum width of field for the result set *) 304 | flags : int; (* Flags set *) 305 | decimals : int (* Number of decimals for numeric fields *) 306 | } 307 | 308 | 309 | 310 | (* low level C interface *) 311 | 312 | let defaults = { dbhost = None; dbname = None; dbport = None; dbpwd = None; dbuser = None; dbsocket = None; } 313 | 314 | type protocol = 315 | | PROTOCOL_DEFAULT 316 | | PROTOCOL_TCP 317 | | PROTOCOL_SOCKET 318 | | PROTOCOL_PIPE 319 | | PROTOCOL_MEMORY 320 | 321 | type db_option = 322 | | OPT_COMPRESS 323 | | OPT_NAMED_PIPE 324 | | OPT_LOCAL_INFILE of bool 325 | | OPT_RECONNECT of bool 326 | | OPT_SSL_VERIFY_SERVER_CERT of bool 327 | | REPORT_DATA_TRUNCATION of bool 328 | | SECURE_AUTH of bool 329 | | OPT_PROTOCOL of protocol 330 | | OPT_CONNECT_TIMEOUT of int 331 | | OPT_READ_TIMEOUT of int 332 | | OPT_WRITE_TIMEOUT of int 333 | | INIT_COMMAND of string 334 | | READ_DEFAULT_FILE of string 335 | | READ_DEFAULT_GROUP of string 336 | | SET_CHARSET_DIR of string 337 | | SET_CHARSET_NAME of string 338 | | SHARED_MEMORY_BASE_NAME of string 339 | | OPT_FOUND_ROWS 340 | 341 | external connect : db_option list -> db -> dbd = "db_connect" 342 | 343 | let connect ?(options=[]) db = connect options db 344 | 345 | let quick_connect ?options ?host ?database ?port ?password ?user ?socket () = 346 | connect ?options { dbhost = host; dbname = database; dbport = port; dbpwd = password; dbuser = user; dbsocket = socket; } 347 | 348 | external change_user : dbd -> db -> unit = "db_change_user" 349 | 350 | let quick_change ?user ?password ?database conn = 351 | change_user conn { defaults with dbuser = user; dbpwd = password; dbname = database } 352 | 353 | external select_db : dbd -> string -> unit = "db_select_db" 354 | external list_dbs : dbd -> ?pat:string -> unit -> string array option = "db_list_dbs" 355 | external disconnect : dbd -> unit = "db_disconnect" 356 | external ping : dbd -> unit = "db_ping" 357 | external exec : dbd -> string -> result = "db_exec" 358 | external real_status : dbd -> int = "db_status" 359 | external errmsg : dbd -> string option = "db_errmsg" 360 | external db_escape : string -> string = "db_escape" 361 | let escape s = 362 | init (); (* libmariadb crashes without init *) 363 | db_escape s 364 | external real_escape: dbd -> string -> string = "db_real_escape" 365 | external set_charset: dbd -> string -> unit = "db_set_charset" 366 | external fetch : result -> string option array option = "db_fetch" 367 | external to_row : result -> int64 -> unit = "db_to_row" 368 | external size : result -> int64 = "db_size" 369 | external affected : dbd -> int64 = "db_affected" 370 | external insert_id: dbd -> int64 = "db_insert_id" 371 | external fields : result -> int = "db_fields" 372 | external client_info : unit -> string = "db_client_info" (* works without init *) 373 | external host_info : dbd -> string = "db_host_info" 374 | external server_info : dbd -> string = "db_server_info" 375 | external proto_info : dbd -> int = "db_proto_info" 376 | external fetch_field : result -> field option = "db_fetch_field" 377 | external fetch_fields : result -> field array option = "db_fetch_fields" 378 | external fetch_field_dir : result -> int -> field option = "db_fetch_field_dir" 379 | 380 | let status dbd = 381 | let x = real_status dbd in 382 | match x with 383 | | 0 -> StatusOK 384 | | 1065 -> StatusEmpty 385 | | _ -> StatusError (error_of_int x) 386 | 387 | let errno dbd = error_of_int (real_status dbd) 388 | 389 | (* [sub start len str] returns integer obtained from substring of length 390 | [len] from [str] *) 391 | 392 | let sub start len str = int_of_string (String.sub str ~pos:start ~len) 393 | 394 | (* xxx2ml parses a string returned from a MySQL field typed xxx and turns it into a 395 | corresponding OCaml value. 396 | 397 | MySQL uses the following representations for date/time values 398 | 399 | DATETIME yyyy-mm-dd hh:mm:ss 400 | DATE yyyy-mm-dd' 401 | TIME hh:mm:ss' 402 | YEAR yyyy' 403 | TIMESTAMP YYYYMMDDHHMMSS' 404 | *) 405 | 406 | 407 | let int2ml str = int_of_string str 408 | let decimal2ml str = str 409 | let int322ml str = Int32.of_string str 410 | let int642ml str = Int64.of_string str 411 | let nativeint2ml str = Nativeint.of_string str 412 | let float2ml str = float_of_string str 413 | let str2ml str = str 414 | let enum2ml str = str 415 | let blob2ml str = str 416 | 417 | (* [set2ml str] parses a comma separated list of words in [str] and 418 | returns them in a list. MySQL uses this format for sets of values 419 | *) 420 | 421 | let set2ml str = 422 | let rec wsp acc i = 423 | if i = String.length str then acc else 424 | match str.[i] with 425 | | ' ' | '\t' | '\n' | '\r' | ',' -> wsp acc (i + 1) 426 | | _ -> loop acc i (i+1) 427 | and loop acc p_start i = 428 | if i = String.length str then (String.sub str p_start (String.length str - p_start)) :: acc else 429 | match str.[i] with 430 | | ' ' | '\t' | '\n' | '\r' | ',' -> wsp (String.sub str p_start (i - p_start) :: acc) (i+1) 431 | | _ -> loop acc p_start (i+1) 432 | in 433 | List.rev (wsp [] 0) 434 | 435 | let datetime2ml str = 436 | assert (String.length str = 19); 437 | 438 | let year = sub 0 4 str in 439 | let month = sub 5 2 str in 440 | let day = sub 8 2 str in 441 | let hour = sub 11 2 str in 442 | let minute = sub 14 2 str in 443 | let second = sub 17 2 str in 444 | (year,month,day,hour,minute,second) 445 | 446 | let date2ml str = 447 | assert (String.length str = 10); 448 | let year = sub 0 4 str in 449 | let month = sub 5 2 str in 450 | let day = sub 8 2 str in 451 | (year,month,day) 452 | 453 | let time2ml str = 454 | assert (String.length str = 8); 455 | 456 | let hour = sub 0 2 str in 457 | let minute = sub 3 2 str in 458 | let second = sub 6 2 str in 459 | (hour,minute,second) 460 | 461 | let year2ml str = 462 | assert (String.length str = 4); 463 | sub 0 4 str 464 | 465 | let timestamp2ml str = 466 | assert (String.length str = 14); 467 | let year = sub 0 4 str in 468 | let month = sub 4 2 str in 469 | let day = sub 6 2 str in 470 | let hour = sub 8 2 str in 471 | let minute = sub 10 2 str in 472 | let second = sub 12 2 str in 473 | (year,month,day,hour,minute,second) 474 | 475 | 476 | (* [opt f v] applies [f] to optional value [v]. Use this to fetch 477 | data of known type from database fields which might be NULL: 478 | [opt int2ml str] *) 479 | let opt f arg = match arg with 480 | | None -> None 481 | | Some x -> Some (f x) 482 | 483 | (* [not_null f v] applies [f] to [Some v]. Use this to fetch data of known 484 | type from database fields which never can be NULL: [not_null int2ml str] 485 | *) 486 | let not_null f arg = match arg with 487 | | None -> fail "not_null was applied to None" 488 | | Some x -> f x 489 | 490 | 491 | let names result = 492 | Array.init (fields result) ~f:(function offset -> 493 | match fetch_field_dir result offset with 494 | | Some field -> field.name 495 | | None -> "") 496 | 497 | let types result = 498 | Array.init (fields result) ~f:(function offset -> 499 | match fetch_field_dir result offset with 500 | | Some field -> field.ty 501 | | None -> (fail "Unknown type in field")) 502 | 503 | (* [column result] returns a function [col] which fetches columns from 504 | results by column name. [col] has type string -> 'a array -> 'b. 505 | Where the first argument is the name of the column. 506 | 507 | 508 | let r = exec dbd "select * from table" in 509 | let col = col r in 510 | let rec loop = function 511 | None -> [] 512 | Some a -> not_null int2ml (col "label" a) :: loop (fetch r) 513 | in 514 | loop (fetch r) 515 | 516 | *) 517 | 518 | let column result = 519 | let names = names result in 520 | let map = (* maps names to positions *) 521 | match Array.length names with 522 | | 0 -> StrMap.empty 523 | | n -> let rec loop i map = 524 | if i = n 525 | then map 526 | else loop (i+1) (StrMap.add ~key:names.(i) ~data:i map) 527 | in 528 | loop 0 StrMap.empty in 529 | (* return column name from array and apply f *) 530 | let col ~key ~row = 531 | row.(StrMap.find key map) 532 | in 533 | col 534 | 535 | (* ml2xxx encodes OCaml values into strings that match the MysQL syntax of 536 | the corresponding type *) 537 | 538 | let ml2str str = "'" ^ escape str ^ "'" 539 | let ml2rstr conn str = "'" ^ real_escape conn str ^ "'" 540 | let ml2blob = ml2str 541 | let ml2rblob = ml2rstr 542 | let ml2int x = string_of_int x 543 | let ml2decimal x = x 544 | let ml322int x = Int32.to_string x 545 | let ml642int x = Int64.to_string x 546 | let mlnative2int x = Nativeint.to_string x 547 | let ml2float x = string_of_float x 548 | let ml2enum x = escape x 549 | let ml2renum x = real_escape x 550 | let ml2set_filter f x = 551 | let rec loop f = function 552 | | [] -> "" 553 | | [x] -> f x 554 | | x::y::ys -> f x ^ "," ^ loop f (y::ys) 555 | in loop f x 556 | let ml2set x = ml2set_filter escape x 557 | let ml2rset conn x = ml2set_filter (real_escape conn) x 558 | 559 | let ml2datetimel ~year ~month ~day ~hour ~min ~sec = 560 | Printf.sprintf "'%04d-%02d-%02d %02d:%02d:%02d'" 561 | year month day hour min sec 562 | let ml2datetime (year,month,day,hour,min,sec) = 563 | ml2datetimel ~year ~month ~day ~hour ~min ~sec 564 | 565 | let ml2datel ~year ~month ~day = 566 | Printf.sprintf "%04d-%02d-%02d" year month day 567 | let ml2date (year,month,day) = 568 | ml2datel ~year ~month ~day 569 | 570 | let ml2timel ~hour ~min ~sec = 571 | Printf.sprintf "%02d:%02d%02d" hour min sec 572 | let ml2time (hour,min,sec) = 573 | ml2timel ~hour ~min ~sec 574 | 575 | let ml2year yyyy = Printf.sprintf "%04d" yyyy 576 | 577 | let ml2timestampl ~year ~month ~day ~hour ~min ~sec = 578 | Printf.sprintf "%04d%02d%02d%02d%02d%02d" year month day hour min sec 579 | let ml2timestamp (year,month,day,hour,min,sec) = 580 | ml2timestampl ~year ~month ~day ~hour ~min ~sec 581 | 582 | 583 | 584 | (* [values vs] creates from a list of values in MySQL format 585 | a vector (x,y,z,..) for the MySQL values construct *) 586 | 587 | let values vs = 588 | let rec loop arg = match arg with 589 | | [] -> "" 590 | | [x] -> x 591 | | x::y::ys -> x ^ "," ^ loop (y::ys) 592 | in 593 | "(" ^ loop vs ^ ")" 594 | 595 | 596 | (* Apply f to each row or a specific column of the results *) 597 | let iter res ~f = 598 | if size res > Int64.zero then 599 | let rec loop () = 600 | match fetch res with 601 | | Some row -> f row; loop () 602 | | None -> () 603 | in 604 | to_row res Int64.zero; 605 | loop () 606 | 607 | let iter_col res ~key ~f = 608 | let col = column res ~key in 609 | iter res ~f:(function row -> f (col ~row)) 610 | 611 | let iter_cols res ~key ~f = 612 | let col = column res in 613 | iter res ~f:(function row -> f (Array.map key ~f:(function key -> col ~key ~row))) 614 | 615 | let map res ~f = 616 | if size res > Int64.zero then 617 | let rec loop lst = 618 | match fetch res with 619 | | Some row -> loop (f row :: lst) 620 | | None -> lst 621 | in 622 | to_row res Int64.zero; 623 | List.rev (loop []) 624 | else 625 | [] 626 | 627 | let map_col res ~key ~f = 628 | let col = column res ~key in 629 | map res ~f:(function row -> f (col ~row)) 630 | 631 | let map_cols res ~key ~f = 632 | let col = column res in 633 | map res ~f:(function row -> f (Array.map key ~f:(function key -> col ~key ~row))) 634 | 635 | module Prepared = struct 636 | 637 | type stmt 638 | type stmt_result 639 | 640 | external create : dbd -> string -> stmt = "caml_mysql_stmt_prepare" 641 | external execute : stmt -> string array -> stmt_result = "caml_mysql_stmt_execute" 642 | external execute_null : stmt -> string option array -> stmt_result = "caml_mysql_stmt_execute_null" 643 | external affected : stmt -> int64 = "caml_mysql_stmt_affected" 644 | external insert_id : stmt -> int64 = "caml_mysql_stmt_insert_id" 645 | external real_status : stmt -> int = "caml_mysql_stmt_status" 646 | external fetch : stmt_result -> string option array option = "caml_mysql_stmt_fetch" 647 | external result_metadata : stmt -> result = "caml_mysql_stmt_result_metadata" 648 | external close : stmt -> unit = "caml_mysql_stmt_close" 649 | 650 | end 651 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | 2 | GNU LESSER GENERAL PUBLIC LICENSE 3 | Version 2.1, February 1999 4 | 5 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 6 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | [This is the first released version of the Lesser GPL. It also counts 11 | as the successor of the GNU Library Public License, version 2, hence 12 | the version number 2.1.] 13 | 14 | Preamble 15 | 16 | The licenses for most software are designed to take away your 17 | freedom to share and change it. By contrast, the GNU General Public 18 | Licenses are intended to guarantee your freedom to share and change 19 | free software--to make sure the software is free for all its users. 20 | 21 | This license, the Lesser General Public License, applies to some 22 | specially designated software packages--typically libraries--of the 23 | Free Software Foundation and other authors who decide to use it. You 24 | can use it too, but we suggest you first think carefully about whether 25 | this license or the ordinary General Public License is the better 26 | strategy to use in any particular case, based on the explanations 27 | below. 28 | 29 | When we speak of free software, we are referring to freedom of use, 30 | not price. Our General Public Licenses are designed to make sure that 31 | you have the freedom to distribute copies of free software (and charge 32 | for this service if you wish); that you receive source code or can get 33 | it if you want it; that you can change the software and use pieces of 34 | it in new free programs; and that you are informed that you can do 35 | these things. 36 | 37 | To protect your rights, we need to make restrictions that forbid 38 | distributors to deny you these rights or to ask you to surrender these 39 | rights. These restrictions translate to certain responsibilities for 40 | you if you distribute copies of the library or if you modify it. 41 | 42 | For example, if you distribute copies of the library, whether gratis 43 | or for a fee, you must give the recipients all the rights that we gave 44 | you. You must make sure that they, too, receive or can get the source 45 | code. If you link other code with the library, you must provide 46 | complete object files to the recipients, so that they can relink them 47 | with the library after making changes to the library and recompiling 48 | it. And you must show them these terms so they know their rights. 49 | 50 | We protect your rights with a two-step method: (1) we copyright the 51 | library, and (2) we offer you this license, which gives you legal 52 | permission to copy, distribute and/or modify the library. 53 | 54 | To protect each distributor, we want to make it very clear that 55 | there is no warranty for the free library. Also, if the library is 56 | modified by someone else and passed on, the recipients should know 57 | that what they have is not the original version, so that the original 58 | author's reputation will not be affected by problems that might be 59 | introduced by others. 60 | ^L 61 | Finally, software patents pose a constant threat to the existence of 62 | any free program. We wish to make sure that a company cannot 63 | effectively restrict the users of a free program by obtaining a 64 | restrictive license from a patent holder. Therefore, we insist that 65 | any patent license obtained for a version of the library must be 66 | consistent with the full freedom of use specified in this license. 67 | 68 | Most GNU software, including some libraries, is covered by the 69 | ordinary GNU General Public License. This license, the GNU Lesser 70 | General Public License, applies to certain designated libraries, and 71 | is quite different from the ordinary General Public License. We use 72 | this license for certain libraries in order to permit linking those 73 | libraries into non-free programs. 74 | 75 | When a program is linked with a library, whether statically or using 76 | a shared library, the combination of the two is legally speaking a 77 | combined work, a derivative of the original library. The ordinary 78 | General Public License therefore permits such linking only if the 79 | entire combination fits its criteria of freedom. The Lesser General 80 | Public License permits more lax criteria for linking other code with 81 | the library. 82 | 83 | We call this license the "Lesser" General Public License because it 84 | does Less to protect the user's freedom than the ordinary General 85 | Public License. It also provides other free software developers Less 86 | of an advantage over competing non-free programs. These disadvantages 87 | are the reason we use the ordinary General Public License for many 88 | libraries. However, the Lesser license provides advantages in certain 89 | special circumstances. 90 | 91 | For example, on rare occasions, there may be a special need to 92 | encourage the widest possible use of a certain library, so that it 93 | becomes a de-facto standard. To achieve this, non-free programs must 94 | be allowed to use the library. A more frequent case is that a free 95 | library does the same job as widely used non-free libraries. In this 96 | case, there is little to gain by limiting the free library to free 97 | software only, so we use the Lesser General Public License. 98 | 99 | In other cases, permission to use a particular library in non-free 100 | programs enables a greater number of people to use a large body of 101 | free software. For example, permission to use the GNU C Library in 102 | non-free programs enables many more people to use the whole GNU 103 | operating system, as well as its variant, the GNU/Linux operating 104 | system. 105 | 106 | Although the Lesser General Public License is Less protective of the 107 | users' freedom, it does ensure that the user of a program that is 108 | linked with the Library has the freedom and the wherewithal to run 109 | that program using a modified version of the Library. 110 | 111 | The precise terms and conditions for copying, distribution and 112 | modification follow. Pay close attention to the difference between a 113 | "work based on the library" and a "work that uses the library". The 114 | former contains code derived from the library, whereas the latter must 115 | be combined with the library in order to run. 116 | ^L 117 | GNU LESSER GENERAL PUBLIC LICENSE 118 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 119 | 120 | 0. This License Agreement applies to any software library or other 121 | program which contains a notice placed by the copyright holder or 122 | other authorized party saying it may be distributed under the terms of 123 | this Lesser General Public License (also called "this License"). 124 | Each licensee is addressed as "you". 125 | 126 | A "library" means a collection of software functions and/or data 127 | prepared so as to be conveniently linked with application programs 128 | (which use some of those functions and data) to form executables. 129 | 130 | The "Library", below, refers to any such software library or work 131 | which has been distributed under these terms. A "work based on the 132 | Library" means either the Library or any derivative work under 133 | copyright law: that is to say, a work containing the Library or a 134 | portion of it, either verbatim or with modifications and/or translated 135 | straightforwardly into another language. (Hereinafter, translation is 136 | included without limitation in the term "modification".) 137 | 138 | "Source code" for a work means the preferred form of the work for 139 | making modifications to it. For a library, complete source code means 140 | all the source code for all modules it contains, plus any associated 141 | interface definition files, plus the scripts used to control 142 | compilation and installation of the library. 143 | 144 | Activities other than copying, distribution and modification are not 145 | covered by this License; they are outside its scope. The act of 146 | running a program using the Library is not restricted, and output from 147 | such a program is covered only if its contents constitute a work based 148 | on the Library (independent of the use of the Library in a tool for 149 | writing it). Whether that is true depends on what the Library does 150 | and what the program that uses the Library does. 151 | 152 | 1. You may copy and distribute verbatim copies of the Library's 153 | complete source code as you receive it, in any medium, provided that 154 | you conspicuously and appropriately publish on each copy an 155 | appropriate copyright notice and disclaimer of warranty; keep intact 156 | all the notices that refer to this License and to the absence of any 157 | warranty; and distribute a copy of this License along with the 158 | Library. 159 | 160 | You may charge a fee for the physical act of transferring a copy, 161 | and you may at your option offer warranty protection in exchange for a 162 | fee. 163 | 164 | 2. You may modify your copy or copies of the Library or any portion 165 | of it, thus forming a work based on the Library, and copy and 166 | distribute such modifications or work under the terms of Section 1 167 | above, provided that you also meet all of these conditions: 168 | 169 | a) The modified work must itself be a software library. 170 | 171 | b) You must cause the files modified to carry prominent notices 172 | stating that you changed the files and the date of any change. 173 | 174 | c) You must cause the whole of the work to be licensed at no 175 | charge to all third parties under the terms of this License. 176 | 177 | d) If a facility in the modified Library refers to a function or a 178 | table of data to be supplied by an application program that uses 179 | the facility, other than as an argument passed when the facility 180 | is invoked, then you must make a good faith effort to ensure that, 181 | in the event an application does not supply such function or 182 | table, the facility still operates, and performs whatever part of 183 | its purpose remains meaningful. 184 | 185 | (For example, a function in a library to compute square roots has 186 | a purpose that is entirely well-defined independent of the 187 | application. Therefore, Subsection 2d requires that any 188 | application-supplied function or table used by this function must 189 | be optional: if the application does not supply it, the square 190 | root function must still compute square roots.) 191 | 192 | These requirements apply to the modified work as a whole. If 193 | identifiable sections of that work are not derived from the Library, 194 | and can be reasonably considered independent and separate works in 195 | themselves, then this License, and its terms, do not apply to those 196 | sections when you distribute them as separate works. But when you 197 | distribute the same sections as part of a whole which is a work based 198 | on the Library, the distribution of the whole must be on the terms of 199 | this License, whose permissions for other licensees extend to the 200 | entire whole, and thus to each and every part regardless of who wrote 201 | it. 202 | 203 | Thus, it is not the intent of this section to claim rights or contest 204 | your rights to work written entirely by you; rather, the intent is to 205 | exercise the right to control the distribution of derivative or 206 | collective works based on the Library. 207 | 208 | In addition, mere aggregation of another work not based on the Library 209 | with the Library (or with a work based on the Library) on a volume of 210 | a storage or distribution medium does not bring the other work under 211 | the scope of this License. 212 | 213 | 3. You may opt to apply the terms of the ordinary GNU General Public 214 | License instead of this License to a given copy of the Library. To do 215 | this, you must alter all the notices that refer to this License, so 216 | that they refer to the ordinary GNU General Public License, version 2, 217 | instead of to this License. (If a newer version than version 2 of the 218 | ordinary GNU General Public License has appeared, then you can specify 219 | that version instead if you wish.) Do not make any other change in 220 | these notices. 221 | ^L 222 | Once this change is made in a given copy, it is irreversible for 223 | that copy, so the ordinary GNU General Public License applies to all 224 | subsequent copies and derivative works made from that copy. 225 | 226 | This option is useful when you wish to copy part of the code of 227 | the Library into a program that is not a library. 228 | 229 | 4. You may copy and distribute the Library (or a portion or 230 | derivative of it, under Section 2) in object code or executable form 231 | under the terms of Sections 1 and 2 above provided that you accompany 232 | it with the complete corresponding machine-readable source code, which 233 | must be distributed under the terms of Sections 1 and 2 above on a 234 | medium customarily used for software interchange. 235 | 236 | If distribution of object code is made by offering access to copy 237 | from a designated place, then offering equivalent access to copy the 238 | source code from the same place satisfies the requirement to 239 | distribute the source code, even though third parties are not 240 | compelled to copy the source along with the object code. 241 | 242 | 5. A program that contains no derivative of any portion of the 243 | Library, but is designed to work with the Library by being compiled or 244 | linked with it, is called a "work that uses the Library". Such a 245 | work, in isolation, is not a derivative work of the Library, and 246 | therefore falls outside the scope of this License. 247 | 248 | However, linking a "work that uses the Library" with the Library 249 | creates an executable that is a derivative of the Library (because it 250 | contains portions of the Library), rather than a "work that uses the 251 | library". The executable is therefore covered by this License. 252 | Section 6 states terms for distribution of such executables. 253 | 254 | When a "work that uses the Library" uses material from a header file 255 | that is part of the Library, the object code for the work may be a 256 | derivative work of the Library even though the source code is not. 257 | Whether this is true is especially significant if the work can be 258 | linked without the Library, or if the work is itself a library. The 259 | threshold for this to be true is not precisely defined by law. 260 | 261 | If such an object file uses only numerical parameters, data 262 | structure layouts and accessors, and small macros and small inline 263 | functions (ten lines or less in length), then the use of the object 264 | file is unrestricted, regardless of whether it is legally a derivative 265 | work. (Executables containing this object code plus portions of the 266 | Library will still fall under Section 6.) 267 | 268 | Otherwise, if the work is a derivative of the Library, you may 269 | distribute the object code for the work under the terms of Section 6. 270 | Any executables containing that work also fall under Section 6, 271 | whether or not they are linked directly with the Library itself. 272 | ^L 273 | 6. As an exception to the Sections above, you may also combine or 274 | link a "work that uses the Library" with the Library to produce a 275 | work containing portions of the Library, and distribute that work 276 | under terms of your choice, provided that the terms permit 277 | modification of the work for the customer's own use and reverse 278 | engineering for debugging such modifications. 279 | 280 | You must give prominent notice with each copy of the work that the 281 | Library is used in it and that the Library and its use are covered by 282 | this License. You must supply a copy of this License. If the work 283 | during execution displays copyright notices, you must include the 284 | copyright notice for the Library among them, as well as a reference 285 | directing the user to the copy of this License. Also, you must do one 286 | of these things: 287 | 288 | a) Accompany the work with the complete corresponding 289 | machine-readable source code for the Library including whatever 290 | changes were used in the work (which must be distributed under 291 | Sections 1 and 2 above); and, if the work is an executable linked 292 | with the Library, with the complete machine-readable "work that 293 | uses the Library", as object code and/or source code, so that the 294 | user can modify the Library and then relink to produce a modified 295 | executable containing the modified Library. (It is understood 296 | that the user who changes the contents of definitions files in the 297 | Library will not necessarily be able to recompile the application 298 | to use the modified definitions.) 299 | 300 | b) Use a suitable shared library mechanism for linking with the 301 | Library. A suitable mechanism is one that (1) uses at run time a 302 | copy of the library already present on the user's computer system, 303 | rather than copying library functions into the executable, and (2) 304 | will operate properly with a modified version of the library, if 305 | the user installs one, as long as the modified version is 306 | interface-compatible with the version that the work was made with. 307 | 308 | c) Accompany the work with a written offer, valid for at least 309 | three years, to give the same user the materials specified in 310 | Subsection 6a, above, for a charge no more than the cost of 311 | performing this distribution. 312 | 313 | d) If distribution of the work is made by offering access to copy 314 | from a designated place, offer equivalent access to copy the above 315 | specified materials from the same place. 316 | 317 | e) Verify that the user has already received a copy of these 318 | materials or that you have already sent this user a copy. 319 | 320 | For an executable, the required form of the "work that uses the 321 | Library" must include any data and utility programs needed for 322 | reproducing the executable from it. However, as a special exception, 323 | the materials to be distributed need not include anything that is 324 | normally distributed (in either source or binary form) with the major 325 | components (compiler, kernel, and so on) of the operating system on 326 | which the executable runs, unless that component itself accompanies 327 | the executable. 328 | 329 | It may happen that this requirement contradicts the license 330 | restrictions of other proprietary libraries that do not normally 331 | accompany the operating system. Such a contradiction means you cannot 332 | use both them and the Library together in an executable that you 333 | distribute. 334 | ^L 335 | 7. You may place library facilities that are a work based on the 336 | Library side-by-side in a single library together with other library 337 | facilities not covered by this License, and distribute such a combined 338 | library, provided that the separate distribution of the work based on 339 | the Library and of the other library facilities is otherwise 340 | permitted, and provided that you do these two things: 341 | 342 | a) Accompany the combined library with a copy of the same work 343 | based on the Library, uncombined with any other library 344 | facilities. This must be distributed under the terms of the 345 | Sections above. 346 | 347 | b) Give prominent notice with the combined library of the fact 348 | that part of it is a work based on the Library, and explaining 349 | where to find the accompanying uncombined form of the same work. 350 | 351 | 8. You may not copy, modify, sublicense, link with, or distribute 352 | the Library except as expressly provided under this License. Any 353 | attempt otherwise to copy, modify, sublicense, link with, or 354 | distribute the Library is void, and will automatically terminate your 355 | rights under this License. However, parties who have received copies, 356 | or rights, from you under this License will not have their licenses 357 | terminated so long as such parties remain in full compliance. 358 | 359 | 9. You are not required to accept this License, since you have not 360 | signed it. However, nothing else grants you permission to modify or 361 | distribute the Library or its derivative works. These actions are 362 | prohibited by law if you do not accept this License. Therefore, by 363 | modifying or distributing the Library (or any work based on the 364 | Library), you indicate your acceptance of this License to do so, and 365 | all its terms and conditions for copying, distributing or modifying 366 | the Library or works based on it. 367 | 368 | 10. Each time you redistribute the Library (or any work based on the 369 | Library), the recipient automatically receives a license from the 370 | original licensor to copy, distribute, link with or modify the Library 371 | subject to these terms and conditions. You may not impose any further 372 | restrictions on the recipients' exercise of the rights granted herein. 373 | You are not responsible for enforcing compliance by third parties with 374 | this License. 375 | ^L 376 | 11. If, as a consequence of a court judgment or allegation of patent 377 | infringement or for any other reason (not limited to patent issues), 378 | conditions are imposed on you (whether by court order, agreement or 379 | otherwise) that contradict the conditions of this License, they do not 380 | excuse you from the conditions of this License. If you cannot 381 | distribute so as to satisfy simultaneously your obligations under this 382 | License and any other pertinent obligations, then as a consequence you 383 | may not distribute the Library at all. For example, if a patent 384 | license would not permit royalty-free redistribution of the Library by 385 | all those who receive copies directly or indirectly through you, then 386 | the only way you could satisfy both it and this License would be to 387 | refrain entirely from distribution of the Library. 388 | 389 | If any portion of this section is held invalid or unenforceable under 390 | any particular circumstance, the balance of the section is intended to 391 | apply, and the section as a whole is intended to apply in other 392 | circumstances. 393 | 394 | It is not the purpose of this section to induce you to infringe any 395 | patents or other property right claims or to contest validity of any 396 | such claims; this section has the sole purpose of protecting the 397 | integrity of the free software distribution system which is 398 | implemented by public license practices. Many people have made 399 | generous contributions to the wide range of software distributed 400 | through that system in reliance on consistent application of that 401 | system; it is up to the author/donor to decide if he or she is willing 402 | to distribute software through any other system and a licensee cannot 403 | impose that choice. 404 | 405 | This section is intended to make thoroughly clear what is believed to 406 | be a consequence of the rest of this License. 407 | 408 | 12. If the distribution and/or use of the Library is restricted in 409 | certain countries either by patents or by copyrighted interfaces, the 410 | original copyright holder who places the Library under this License 411 | may add an explicit geographical distribution limitation excluding those 412 | countries, so that distribution is permitted only in or among 413 | countries not thus excluded. In such case, this License incorporates 414 | the limitation as if written in the body of this License. 415 | 416 | 13. The Free Software Foundation may publish revised and/or new 417 | versions of the Lesser General Public License from time to time. 418 | Such new versions will be similar in spirit to the present version, 419 | but may differ in detail to address new problems or concerns. 420 | 421 | Each version is given a distinguishing version number. If the Library 422 | specifies a version number of this License which applies to it and 423 | "any later version", you have the option of following the terms and 424 | conditions either of that version or of any later version published by 425 | the Free Software Foundation. If the Library does not specify a 426 | license version number, you may choose any version ever published by 427 | the Free Software Foundation. 428 | ^L 429 | 14. If you wish to incorporate parts of the Library into other free 430 | programs whose distribution conditions are incompatible with these, 431 | write to the author to ask for permission. For software which is 432 | copyrighted by the Free Software Foundation, write to the Free 433 | Software Foundation; we sometimes make exceptions for this. Our 434 | decision will be guided by the two goals of preserving the free status 435 | of all derivatives of our free software and of promoting the sharing 436 | and reuse of software generally. 437 | 438 | NO WARRANTY 439 | 440 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 441 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 442 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 443 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 444 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 445 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 446 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 447 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 448 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 449 | 450 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 451 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 452 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 453 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 454 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 455 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 456 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 457 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 458 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 459 | DAMAGES. 460 | 461 | END OF TERMS AND CONDITIONS 462 | ^L 463 | How to Apply These Terms to Your New Libraries 464 | 465 | If you develop a new library, and you want it to be of the greatest 466 | possible use to the public, we recommend making it free software that 467 | everyone can redistribute and change. You can do so by permitting 468 | redistribution under these terms (or, alternatively, under the terms 469 | of the ordinary General Public License). 470 | 471 | To apply these terms, attach the following notices to the library. 472 | It is safest to attach them to the start of each source file to most 473 | effectively convey the exclusion of warranty; and each file should 474 | have at least the "copyright" line and a pointer to where the full 475 | notice is found. 476 | 477 | 478 | 479 | Copyright (C) 480 | 481 | This library is free software; you can redistribute it and/or 482 | modify it under the terms of the GNU Lesser General Public 483 | License as published by the Free Software Foundation; either 484 | version 2.1 of the License, or (at your option) any later version. 485 | 486 | This library is distributed in the hope that it will be useful, 487 | but WITHOUT ANY WARRANTY; without even the implied warranty of 488 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 489 | Lesser General Public License for more details. 490 | 491 | You should have received a copy of the GNU Lesser General Public 492 | License along with this library; if not, write to the Free Software 493 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 494 | 495 | Also add information on how to contact you by electronic and paper mail. 496 | 497 | You should also get your employer (if you work as a programmer) or 498 | your school, if any, to sign a "copyright disclaimer" for the library, 499 | if necessary. Here is a sample; alter the names: 500 | 501 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 502 | library `Frob' (a library for tweaking knobs) written by James 503 | Random Hacker. 504 | 505 | , 1 April 1990 506 | Ty Coon, President of Vice 507 | 508 | That's all there is to it! 509 | 510 | 511 | -------------------------------------------------------------------------------- /mysql_stubs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This module provides access to MySQL from Objective Caml. 3 | */ 4 | 5 | 6 | #include /* sprintf */ 7 | #include 8 | #include 9 | 10 | /* OCaml runtime system */ 11 | #define CAML_NAME_SPACE 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #ifdef HAVE_CONFIG_H 21 | #include "config.h" 22 | /* else attempt without (think msvc build) */ 23 | #endif 24 | 25 | /* MySQL API */ 26 | 27 | #if defined(_WIN32) 28 | /* mode_t typedef conflict (mingw and mysql) */ 29 | #ifdef __MINGW32__ 30 | #include 31 | #else 32 | #include 33 | #endif 34 | #endif 35 | 36 | #if defined(HAVE_MYSQL_MYSQL_H) 37 | #include 38 | #else 39 | #include 40 | #endif 41 | 42 | #define EXTERNAL /* dummy to highlight fn's exported to ML */ 43 | 44 | #ifdef CAML_TEST_GC_SAFE 45 | #include 46 | #define caml_enter_blocking_section() if (1) { caml_enter_blocking_section(); sleep(1); } 47 | #endif 48 | 49 | /* Abstract types 50 | * 51 | * dbd - data base descriptor 52 | * 53 | * header with Abstract_tag 54 | * 0: MYSQL* 55 | * 1: bool (open == true, closed == false) 56 | * 57 | * res - result returned from query/exec 58 | * 59 | * header with Final_tag 60 | * 0: finalization function for 1 61 | * 1: MYSQL_RES* 62 | * 63 | */ 64 | 65 | /* macros to access C values stored inside the abstract values */ 66 | 67 | #define DBDmysql(x) ((MYSQL*)(Field(x,1))) 68 | #define DBDopen(x) (Field(x,2)) 69 | #define RESval(x) (*(MYSQL_RES**)Data_custom_val(x)) 70 | 71 | #define STMTval(x) (*(MYSQL_STMT**)Data_custom_val(x)) 72 | #define ROWval(x) (*(row_t**)Data_custom_val(x)) 73 | 74 | static void mysqlfailwith(char *err) Noreturn; 75 | static void mysqlfailmsg(const char *fmt, ...) Noreturn; 76 | 77 | static void 78 | mysqlfailwith(char *err) 79 | { 80 | caml_raise_with_string(*caml_named_value("mysql error"), err); 81 | } 82 | 83 | static void 84 | mysqlfailmsg(const char *fmt, ...) 85 | { 86 | char buf[1024]; 87 | va_list args; 88 | 89 | va_start(args, fmt); 90 | vsnprintf(buf, sizeof buf, fmt, args); 91 | va_end(args); 92 | 93 | caml_raise_with_string(*caml_named_value("mysql error"), buf); 94 | } 95 | 96 | #define Val_none Val_int(0) 97 | 98 | static inline value 99 | Val_some( value v ) 100 | { 101 | CAMLparam1( v ); 102 | CAMLlocal1( some ); 103 | some = caml_alloc_small(1, 0); 104 | Field(some, 0) = v; 105 | CAMLreturn( some ); 106 | } 107 | 108 | #define Some_val(v) Field(v,0) 109 | 110 | static int 111 | int_option(value v) 112 | { 113 | if (v == Val_none) 114 | return 0; 115 | else 116 | return Int_val(Some_val(v)); 117 | } 118 | 119 | 120 | /* 121 | * strdup_option copies a char* from an ML string option value. Returns 122 | * NULL for None. 123 | */ 124 | 125 | static char* 126 | strdup_option(value v) 127 | { 128 | if (v == Val_none) 129 | return (char*) NULL; 130 | else 131 | return strdup(String_val(Some_val(v))); 132 | } 133 | 134 | /* 135 | * val_str_option creates a string option value from a char* (NONE for 136 | * NULL) -- dual to str_option(). 137 | */ 138 | 139 | static value 140 | val_str_option(const char* s, unsigned long length) 141 | { 142 | CAMLparam0(); 143 | CAMLlocal1(v); 144 | 145 | if (!s) 146 | CAMLreturn(Val_none); 147 | else 148 | { 149 | v = caml_alloc_string(length); 150 | memcpy(String_val(v), s, length); 151 | 152 | CAMLreturn(Val_some(v)); 153 | } 154 | } 155 | 156 | /* check_db checks that the data base connection is still open. The 157 | * open flag is reset by db_disconnect(). 158 | */ 159 | 160 | static inline MYSQL* 161 | check_db(value dbd, const char *fun) 162 | { 163 | if (!Bool_val(DBDopen(dbd))) 164 | mysqlfailmsg("Mysql.%s called with closed connection", fun); 165 | return DBDmysql(dbd); 166 | } 167 | 168 | 169 | static void 170 | conn_finalize(value dbd) 171 | { 172 | if (Bool_val(DBDopen(dbd))) 173 | { 174 | MYSQL* db = DBDmysql(dbd); 175 | caml_enter_blocking_section(); 176 | mysql_close(db); 177 | caml_leave_blocking_section(); 178 | } 179 | } 180 | 181 | /* db_connect opens a data base connection and returns an abstract 182 | * connection object. 183 | */ 184 | 185 | static unsigned int ml_mysql_protocol_type[] = { 186 | MYSQL_PROTOCOL_DEFAULT, MYSQL_PROTOCOL_TCP, MYSQL_PROTOCOL_SOCKET, 187 | MYSQL_PROTOCOL_PIPE, MYSQL_PROTOCOL_MEMORY 188 | }; 189 | 190 | #define SET_OPTION(option, p) if (0 != mysql_options(init,MYSQL_##option,p)) mysqlfailwith( "MYSQL_" #option ); break 191 | #define SET_OPTION_BOOL(option) option_bool = Bool_val(v); SET_OPTION(option, &option_bool) 192 | #define SET_OPTION_INT(option) option_int = Int_val(v); SET_OPTION(option, &option_int) 193 | #define SET_OPTION_STR(option) SET_OPTION(option, String_val(v)) 194 | #define SET_CLIENT_FLAG(flag) client_flag |= flag; break 195 | 196 | EXTERNAL value 197 | db_connect(value options, value args) 198 | 199 | { 200 | CAMLparam2(options, args); 201 | CAMLlocal2(res, v); 202 | char *host = NULL; 203 | char *db = NULL; 204 | unsigned int port = 0; 205 | char *pwd = NULL; 206 | char *user = NULL; 207 | char *socket = NULL; 208 | MYSQL *init; 209 | MYSQL *mysql; 210 | unsigned int option_int; 211 | my_bool option_bool; 212 | unsigned long client_flag = 0; 213 | 214 | init = mysql_init(NULL); 215 | if (!init) 216 | { 217 | mysqlfailwith("connect failed"); 218 | } 219 | else 220 | { 221 | while (options != Val_emptylist) 222 | { 223 | if (Is_block(Field(options,0))) 224 | { 225 | v = Field(Field(options,0),0); 226 | switch (Tag_val(Field(options,0))) 227 | { 228 | case 0: SET_OPTION_BOOL(OPT_LOCAL_INFILE); 229 | case 1: SET_OPTION_BOOL(OPT_RECONNECT); 230 | case 2: SET_OPTION_BOOL(OPT_SSL_VERIFY_SERVER_CERT); 231 | case 3: SET_OPTION_BOOL(REPORT_DATA_TRUNCATION); 232 | case 4: SET_OPTION_BOOL(SECURE_AUTH); 233 | case 5: SET_OPTION(OPT_PROTOCOL, &ml_mysql_protocol_type[Int_val(v)]); 234 | case 6: SET_OPTION_INT(OPT_CONNECT_TIMEOUT); 235 | case 7: SET_OPTION_INT(OPT_READ_TIMEOUT); 236 | case 8: SET_OPTION_INT(OPT_WRITE_TIMEOUT); 237 | case 9: SET_OPTION_STR(INIT_COMMAND); 238 | case 10: SET_OPTION_STR(READ_DEFAULT_FILE); 239 | case 11: SET_OPTION_STR(READ_DEFAULT_GROUP); 240 | case 12: SET_OPTION_STR(SET_CHARSET_DIR); 241 | case 13: SET_OPTION_STR(SET_CHARSET_NAME); 242 | case 14: SET_OPTION_STR(SHARED_MEMORY_BASE_NAME); 243 | default: 244 | caml_invalid_argument("Mysql.connect: unknown option"); 245 | } 246 | } 247 | else 248 | { 249 | switch (Int_val(Field(options,0))) 250 | { 251 | case 0: SET_OPTION(OPT_COMPRESS, NULL); 252 | case 1: SET_OPTION(OPT_NAMED_PIPE, NULL); 253 | case 2: SET_CLIENT_FLAG(CLIENT_FOUND_ROWS); 254 | default: caml_invalid_argument("Mysql.connect: unknown option"); 255 | } 256 | } 257 | options = Field(options, 1); 258 | } 259 | 260 | host = strdup_option(Field(args,0)); 261 | db = strdup_option(Field(args,1)); 262 | port = (unsigned int) int_option(Field(args,2)); 263 | pwd = strdup_option(Field(args,3)); 264 | user = strdup_option(Field(args,4)); 265 | socket = strdup_option(Field(args,5)); 266 | 267 | caml_enter_blocking_section(); 268 | mysql = mysql_real_connect(init ,host ,user 269 | ,pwd ,db ,port 270 | ,socket, client_flag); 271 | caml_leave_blocking_section(); 272 | 273 | free(host); free(db); free(pwd); free(user); free(socket); 274 | 275 | if (!mysql) 276 | { 277 | mysqlfailwith((char*)mysql_error(init)); 278 | } 279 | else 280 | { 281 | res = caml_alloc_final(3, conn_finalize, 0, 1); 282 | Field(res, 1) = (value)mysql; 283 | Field(res, 2) = Val_true; 284 | } 285 | } 286 | CAMLreturn(res); 287 | } 288 | 289 | EXTERNAL value 290 | db_library_init(value v_unit) 291 | { 292 | mysql_library_init(0,NULL,NULL); 293 | return Val_unit; 294 | } 295 | 296 | 297 | EXTERNAL value 298 | db_change_user(value v_dbd, value args) 299 | { 300 | char *db; 301 | char *pwd; 302 | char *user; 303 | my_bool ret; 304 | MYSQL* mysql = check_db(v_dbd,"change_user"); 305 | 306 | db = strdup_option(Field(args,1)); 307 | pwd = strdup_option(Field(args,3)); 308 | user = strdup_option(Field(args,4)); 309 | 310 | caml_enter_blocking_section(); 311 | ret = mysql_change_user(mysql, user, pwd, db); 312 | caml_leave_blocking_section(); 313 | 314 | free(db); free(pwd); free(user); 315 | 316 | if (ret) 317 | mysqlfailmsg("Mysql.change_user: %s", mysql_error(mysql)); 318 | 319 | return Val_unit; 320 | } 321 | 322 | EXTERNAL value 323 | db_list_dbs(value v_dbd, value pattern, value blah) 324 | { 325 | CAMLparam3(v_dbd, pattern, blah); 326 | CAMLlocal1(dbs); 327 | MYSQL* mysql = check_db(v_dbd,"list_dbs"); 328 | char *wild = strdup_option(pattern); 329 | int n, i; 330 | MYSQL_RES *res; 331 | MYSQL_ROW row; 332 | 333 | caml_enter_blocking_section(); 334 | res = mysql_list_dbs(mysql, wild); 335 | caml_leave_blocking_section(); 336 | 337 | free(wild); 338 | 339 | if (!res) 340 | CAMLreturn(Val_none); 341 | 342 | n = mysql_num_rows(res); 343 | 344 | if (n == 0) 345 | { 346 | mysql_free_result(res); 347 | CAMLreturn(Val_none); 348 | } 349 | 350 | dbs = caml_alloc_tuple(n); /* Array */ 351 | i = 0; 352 | while ((row = mysql_fetch_row(res)) != NULL) 353 | { 354 | Store_field(dbs, i, caml_copy_string(row[0])); 355 | i++; 356 | } 357 | 358 | mysql_free_result(res); 359 | CAMLreturn(Val_some(dbs)); 360 | } 361 | 362 | EXTERNAL value 363 | db_select_db(value v_dbd, value v_newdb) 364 | { 365 | CAMLparam2(v_dbd,v_newdb); 366 | MYSQL* mysql = check_db(v_dbd, "select_db"); 367 | char* newdb = strdup(String_val(v_newdb)); 368 | my_bool ret; 369 | 370 | caml_enter_blocking_section(); 371 | ret = mysql_select_db(mysql, newdb); 372 | caml_leave_blocking_section(); 373 | 374 | free(newdb); 375 | 376 | if (ret) 377 | mysqlfailmsg("Mysql.select_db: %s", mysql_error(mysql)); 378 | 379 | CAMLreturn(Val_unit); 380 | } 381 | 382 | /* 383 | * db_disconnect closes a db connection and marks the dbd closed. 384 | */ 385 | 386 | EXTERNAL value 387 | db_disconnect(value dbd) 388 | { 389 | CAMLparam1(dbd); 390 | MYSQL* db = check_db(dbd,"disconnect"); 391 | caml_enter_blocking_section(); 392 | mysql_close(db); 393 | caml_leave_blocking_section(); 394 | Field(dbd, 1) = Val_false; 395 | Field(dbd, 2) = Val_false; /* Mark closed */ 396 | CAMLreturn(Val_unit); 397 | } 398 | 399 | EXTERNAL value 400 | db_ping(value dbd) 401 | { 402 | CAMLparam1(dbd); 403 | MYSQL* db = check_db(dbd,"ping"); 404 | 405 | caml_enter_blocking_section(); 406 | if (mysql_ping(db)) 407 | { 408 | caml_leave_blocking_section(); 409 | mysqlfailmsg("Mysql.ping: %s", mysql_error(db)); 410 | } 411 | caml_leave_blocking_section(); 412 | 413 | CAMLreturn(Val_unit); 414 | } 415 | 416 | /* 417 | * finalize -- this is called when a data base result is garbage 418 | * collected -- frees memory allocated by MySQL. 419 | */ 420 | 421 | static void 422 | res_finalize(value result) 423 | { 424 | MYSQL_RES *res = RESval(result); 425 | if (res) 426 | mysql_free_result(res); 427 | } 428 | 429 | 430 | struct custom_operations res_ops = { 431 | "Mysql Query Results", 432 | res_finalize, 433 | custom_compare_default, 434 | custom_hash_default, 435 | custom_serialize_default, 436 | custom_deserialize_default, 437 | #if defined(custom_compare_ext_default) 438 | custom_compare_ext_default, 439 | #endif 440 | }; 441 | 442 | /* 443 | * db_exec -- execute a SQL query or command. Returns a handle to 444 | * access the result. 445 | */ 446 | 447 | EXTERNAL value 448 | db_exec(value v_dbd, value v_sql) 449 | { 450 | CAMLparam2(v_dbd, v_sql); 451 | CAMLlocal1(res); 452 | MYSQL *mysql = check_db(v_dbd,"exec"); 453 | char* sql = strdup(String_val(v_sql)); 454 | size_t len = caml_string_length(v_sql); 455 | int ret; 456 | 457 | caml_enter_blocking_section(); 458 | ret = mysql_real_query(mysql, sql, len); 459 | caml_leave_blocking_section(); 460 | 461 | free(sql); 462 | 463 | if (ret) 464 | { 465 | mysqlfailmsg("Mysql.exec: %s", mysql_error(mysql)); 466 | } 467 | else 468 | { 469 | res = caml_alloc_custom(&res_ops, sizeof(MYSQL_RES*), 0, 1); 470 | RESval(res) = mysql_store_result(mysql); 471 | } 472 | 473 | CAMLreturn(res); 474 | } 475 | 476 | /* 477 | * db_fetch -- fetch one result tuple, represented as array of string 478 | * options. In case a value is Null, the respective value is None. 479 | * Returns (Some v) in case there is such a result and None otherwise. 480 | * Moves the internal result cursor to the next tuple. 481 | */ 482 | 483 | EXTERNAL value 484 | db_fetch (value result) 485 | { 486 | CAMLparam1(result); 487 | CAMLlocal2(fields, s); 488 | unsigned int i, n; 489 | unsigned long *length; /* array of long */ 490 | MYSQL_RES *res; 491 | MYSQL_ROW row; 492 | 493 | res = RESval(result); 494 | if (!res) 495 | mysqlfailwith("Mysql.fetch: result did not return fetchable data"); 496 | 497 | n = mysql_num_fields(res); 498 | if (n == 0) 499 | mysqlfailwith("Mysql.fetch: no columns"); 500 | 501 | row = mysql_fetch_row(res); 502 | if (!row) 503 | CAMLreturn(Val_none); 504 | 505 | /* create Some([| f1; f2; .. ;fn |]) */ 506 | 507 | length = mysql_fetch_lengths(res); /* length[] */ 508 | fields = caml_alloc_tuple(n); /* array */ 509 | for (i=0;i (int64_t)mysql_num_rows(res)-1) 528 | caml_invalid_argument("Mysql.to_row: offset out of range"); 529 | 530 | mysql_data_seek(res, off); 531 | 532 | return Val_unit; 533 | } 534 | 535 | /* 536 | * db_status -- returns current status (simplistic) 537 | */ 538 | 539 | EXTERNAL value 540 | db_status(value dbd) 541 | { 542 | CAMLparam1(dbd); 543 | CAMLreturn(Val_int(mysql_errno(check_db(dbd,"status")))); 544 | } 545 | 546 | /* 547 | * db_errmsg -- returns string option with last error message (None == 548 | * no error) 549 | */ 550 | 551 | EXTERNAL value 552 | db_errmsg(value dbd) 553 | { 554 | CAMLparam1(dbd); 555 | CAMLlocal1(s); 556 | const char *msg; 557 | 558 | msg = mysql_error(check_db(dbd,"errmsg")); 559 | if (!msg || msg[0] == '\0') 560 | msg = (char*) NULL; 561 | s = val_str_option(msg, msg == (char *) NULL ? 0 : strlen(msg)); 562 | CAMLreturn(s); 563 | } 564 | 565 | /* 566 | * db_escape - takes a string and escape all characters inside which 567 | * must be escaped inside MySQL strings. This helps to construct SQL 568 | * statements easily. Simply returns the new string including the 569 | * quotes. 570 | */ 571 | 572 | EXTERNAL value 573 | db_escape(value str) 574 | { 575 | CAMLparam1(str); 576 | char *s; 577 | char *buf; 578 | int len, esclen; 579 | CAMLlocal1(res); 580 | 581 | s = String_val(str); 582 | len = caml_string_length(str); 583 | buf = (char*)caml_stat_alloc(2*len+1); 584 | esclen = mysql_escape_string(buf,s,len); 585 | 586 | res = caml_alloc_string(esclen); 587 | memcpy(String_val(res), buf, esclen); 588 | caml_stat_free(buf); 589 | 590 | CAMLreturn(res); 591 | } 592 | 593 | EXTERNAL value 594 | db_real_escape(value dbd, value str) 595 | { 596 | CAMLparam2(dbd, str); 597 | char *s; 598 | char *buf; 599 | int len, esclen; 600 | MYSQL *mysql; 601 | CAMLlocal1(res); 602 | 603 | mysql = check_db(dbd, "real_escape"); 604 | 605 | s = String_val(str); 606 | len = caml_string_length(str); 607 | buf = (char*)caml_stat_alloc(2*len+1); 608 | /* 609 | * mysql_real_escape doesn't perform network I/O, 610 | * so no need for blocking section (and extra copying) 611 | * */ 612 | esclen = mysql_real_escape_string(mysql,buf,s,len); 613 | 614 | res = caml_alloc_string(esclen); 615 | memcpy(String_val(res), buf, esclen); 616 | caml_stat_free(buf); 617 | 618 | CAMLreturn(res); 619 | } 620 | 621 | EXTERNAL value 622 | db_set_charset(value dbd, value str) 623 | { 624 | CAMLparam2(dbd, str); 625 | char *s; 626 | MYSQL *mysql; 627 | int res; 628 | 629 | mysql = check_db(dbd, "set_charset"); 630 | 631 | s = strdup(String_val(str)); 632 | caml_enter_blocking_section(); 633 | res = mysql_set_character_set(mysql,s); 634 | free(s); 635 | caml_leave_blocking_section(); 636 | 637 | if (res) 638 | mysqlfailmsg("Mysql.set_charset : %s",mysql_error(mysql)); 639 | 640 | CAMLreturn(Val_unit); 641 | } 642 | 643 | /* 644 | * db_size -- returns the size of the current result (number of rows). 645 | */ 646 | 647 | EXTERNAL value 648 | db_size(value result) 649 | { 650 | CAMLparam1(result); 651 | MYSQL_RES *res; 652 | int64_t size; 653 | 654 | res = RESval(result); 655 | if (!res) 656 | size = 0; 657 | else 658 | size = (int64_t)mysql_num_rows(res); 659 | 660 | CAMLreturn(caml_copy_int64(size)); 661 | } 662 | 663 | EXTERNAL value 664 | db_affected(value dbd) { 665 | CAMLparam1(dbd); 666 | CAMLreturn(caml_copy_int64(mysql_affected_rows(DBDmysql(dbd)))); 667 | } 668 | 669 | EXTERNAL value 670 | db_insert_id(value dbd) { 671 | CAMLparam1(dbd); 672 | CAMLreturn(caml_copy_int64(mysql_insert_id(DBDmysql(dbd)))); 673 | } 674 | 675 | EXTERNAL value 676 | db_fields(value result) 677 | { 678 | MYSQL_RES *res; 679 | long size; 680 | 681 | res = RESval(result); 682 | if (!res) 683 | size = 0; 684 | else 685 | size = (long)(mysql_num_fields(res)); 686 | 687 | return Val_long(size); 688 | } 689 | 690 | EXTERNAL value 691 | db_client_info(value unit) { 692 | CAMLparam1(unit); 693 | CAMLlocal1(info); 694 | info = caml_copy_string(mysql_get_client_info()); 695 | CAMLreturn(info); 696 | } 697 | 698 | EXTERNAL value 699 | db_host_info(value dbd) { 700 | CAMLparam1(dbd); 701 | CAMLlocal1(info); 702 | info = caml_copy_string(mysql_get_host_info(DBDmysql(dbd))); 703 | CAMLreturn(info); 704 | } 705 | 706 | EXTERNAL value 707 | db_server_info(value dbd) { 708 | CAMLparam1(dbd); 709 | CAMLlocal1(info); 710 | info = caml_copy_string(mysql_get_server_info(DBDmysql(dbd))); 711 | CAMLreturn(info); 712 | } 713 | 714 | EXTERNAL value 715 | db_proto_info(value dbd) { 716 | long info = (long)mysql_get_proto_info(DBDmysql(dbd)); 717 | return Val_long(info); 718 | } 719 | 720 | 721 | /* 722 | * type2dbty - maps column types to dbty values which describe the 723 | * column types in OCaml. 724 | */ 725 | 726 | #define INT_TY 0 727 | #define FLOAT_TY 1 728 | #define STRING_TY 2 729 | #define SET_TY 3 730 | #define ENUM_TY 4 731 | #define DATETIME_TY 5 732 | #define DATE_TY 6 733 | #define TIME_TY 7 734 | #define YEAR_TY 8 735 | #define TIMESTAMP_TY 9 736 | #define UNKNOWN_TY 10 737 | #define INT64_TY 11 738 | #define BLOB_TY 12 739 | #define DECIMAL_TY 13 740 | 741 | static value 742 | type2dbty (int type) 743 | { 744 | static struct {int mysql; value caml;} map[] = { 745 | {FIELD_TYPE_DECIMAL , Val_long(DECIMAL_TY)}, 746 | {FIELD_TYPE_TINY , Val_long(INT_TY)}, 747 | {FIELD_TYPE_SHORT , Val_long(INT_TY)}, 748 | {FIELD_TYPE_LONG , Val_long(INT_TY)}, 749 | {FIELD_TYPE_FLOAT , Val_long(FLOAT_TY)}, 750 | {FIELD_TYPE_DOUBLE , Val_long(FLOAT_TY)}, 751 | {FIELD_TYPE_NULL , Val_long(STRING_TY)}, 752 | {FIELD_TYPE_TIMESTAMP , Val_long(TIMESTAMP_TY)}, 753 | {FIELD_TYPE_LONGLONG , Val_long(INT64_TY)}, 754 | {FIELD_TYPE_INT24 , Val_long(INT_TY)}, 755 | {FIELD_TYPE_DATE , Val_long(DATE_TY)}, 756 | {FIELD_TYPE_TIME , Val_long(TIME_TY)}, 757 | {FIELD_TYPE_DATETIME , Val_long(DATETIME_TY)}, 758 | {FIELD_TYPE_YEAR , Val_long(YEAR_TY)}, 759 | {FIELD_TYPE_NEWDATE , Val_long(UNKNOWN_TY)}, 760 | {FIELD_TYPE_ENUM , Val_long(ENUM_TY)}, 761 | {FIELD_TYPE_SET , Val_long(SET_TY)}, 762 | {FIELD_TYPE_TINY_BLOB , Val_long(BLOB_TY)}, 763 | {FIELD_TYPE_MEDIUM_BLOB , Val_long(BLOB_TY)}, 764 | {FIELD_TYPE_LONG_BLOB , Val_long(BLOB_TY)}, 765 | {FIELD_TYPE_BLOB , Val_long(BLOB_TY)}, 766 | {FIELD_TYPE_VAR_STRING , Val_long(STRING_TY)}, 767 | {FIELD_TYPE_STRING , Val_long(STRING_TY)}, 768 | {-1 /*default*/ , Val_long(UNKNOWN_TY)} 769 | }; 770 | int i; 771 | 772 | /* in principle using bsearch() would be better -- but how can 773 | * we know that the left side of the map is properly sorted? 774 | */ 775 | 776 | for (i=0; map[i].mysql != -1 && map[i].mysql != type; i++) 777 | /* empty */ ; 778 | 779 | return map[i].caml; 780 | } 781 | 782 | value 783 | make_field(MYSQL_FIELD *f) { 784 | CAMLparam0(); 785 | CAMLlocal5(out, data, name, table, def); 786 | 787 | name = caml_copy_string(f->name); 788 | 789 | if (f->table) 790 | table = val_str_option(f->table, strlen(f->table)); 791 | else 792 | table = Val_none; 793 | 794 | if (f->def) 795 | def = val_str_option(f->def, strlen(f->def)); 796 | else 797 | def = Val_none; 798 | 799 | data = caml_alloc_small(7, 0); 800 | Field(data, 0) = name; 801 | Field(data, 1) = table; 802 | Field(data, 2) = def; 803 | Field(data, 3) = type2dbty(f->type); 804 | Field(data, 4) = Val_long(f->max_length); 805 | Field(data, 5) = Val_long(f->flags); 806 | Field(data, 6) = Val_long(f->decimals); 807 | 808 | CAMLreturn(data); 809 | } 810 | 811 | EXTERNAL value 812 | db_fetch_field(value result) { 813 | CAMLparam1(result); 814 | CAMLlocal2(field, out); 815 | MYSQL_FIELD *f; 816 | MYSQL_RES *res = RESval(result); 817 | 818 | if (!res) 819 | CAMLreturn(Val_none); 820 | 821 | f = mysql_fetch_field(res); 822 | if (!f) 823 | CAMLreturn(Val_none); 824 | 825 | field = make_field(f); 826 | CAMLreturn(Val_some(field)); 827 | } 828 | 829 | EXTERNAL value 830 | db_fetch_field_dir(value result, value pos) { 831 | CAMLparam2(result, pos); 832 | CAMLlocal2(field, out); 833 | MYSQL_FIELD *f; 834 | MYSQL_RES *res = RESval(result); 835 | 836 | if (!res) 837 | CAMLreturn(Val_none); 838 | 839 | f = mysql_fetch_field_direct(res, Long_val(pos)); 840 | if (!f) 841 | CAMLreturn(Val_none); 842 | 843 | field = make_field(f); 844 | CAMLreturn(Val_some(field)); 845 | } 846 | 847 | 848 | EXTERNAL value 849 | db_fetch_fields(value result) { 850 | CAMLparam1(result); 851 | CAMLlocal1(fields); 852 | MYSQL_RES *res = RESval(result); 853 | MYSQL_FIELD *f; 854 | int i, n; 855 | 856 | if (!res) 857 | CAMLreturn(Val_none); 858 | 859 | n = mysql_num_fields(res); 860 | 861 | if (n == 0) 862 | CAMLreturn(Val_none); 863 | 864 | f = mysql_fetch_fields(res); 865 | 866 | fields = caml_alloc_tuple(n); 867 | 868 | for (i = 0; i < n; i++) { 869 | Store_field(fields, i, make_field(f+i)); 870 | } 871 | 872 | CAMLreturn(Val_some(fields)); 873 | } 874 | 875 | static void 876 | check_stmt(MYSQL_STMT* stmt, char *fun) 877 | { 878 | if (!stmt) 879 | mysqlfailmsg("Mysql.Prepared.%s called with closed statement", fun); 880 | } 881 | 882 | static void 883 | stmt_finalize(value v_stmt) 884 | { 885 | MYSQL_STMT* stmt = STMTval(v_stmt); 886 | if (!stmt) return; 887 | caml_enter_blocking_section(); 888 | mysql_stmt_close(stmt); 889 | caml_leave_blocking_section(); 890 | STMTval(v_stmt) = (MYSQL_STMT*)NULL; 891 | } 892 | 893 | struct custom_operations stmt_ops = { 894 | "Mysql Prepared Statement", 895 | stmt_finalize, 896 | custom_compare_default, 897 | custom_hash_default, 898 | custom_serialize_default, 899 | custom_deserialize_default, 900 | #if defined(custom_compare_ext_default) 901 | custom_compare_ext_default, 902 | #endif 903 | }; 904 | 905 | 906 | EXTERNAL value 907 | caml_mysql_stmt_prepare(value v_dbd, value v_sql) 908 | { 909 | CAMLparam2(v_dbd,v_sql); 910 | CAMLlocal1(res); 911 | int ret = 0; 912 | MYSQL_STMT* stmt = NULL; 913 | MYSQL* db = check_db(v_dbd, "Prepared.create"); 914 | char* sql_c = strdup(String_val(v_sql)); 915 | if (!sql_c) 916 | mysqlfailwith("Mysql.Prepared.create : strdup"); 917 | caml_enter_blocking_section(); 918 | stmt = mysql_stmt_init(db); 919 | if (!stmt) 920 | { 921 | free(sql_c); 922 | caml_leave_blocking_section(); 923 | mysqlfailwith("Mysql.Prepared.create : mysql_stmt_init"); 924 | } 925 | ret = mysql_stmt_prepare(stmt, sql_c, strlen(sql_c)); 926 | free(sql_c); 927 | if (ret) 928 | { 929 | const char* err = mysql_stmt_error(stmt); 930 | char buf[1024]; 931 | snprintf(buf, sizeof buf, "Mysql.Prepared.create : mysql_stmt_prepare = %i. Query : %s. Error : %s",ret,String_val(v_sql),err); 932 | mysql_stmt_close(stmt); 933 | caml_leave_blocking_section(); 934 | mysqlfailwith(buf); 935 | } 936 | caml_leave_blocking_section(); 937 | res = caml_alloc_custom(&stmt_ops, sizeof(MYSQL_STMT*), 0, 1); 938 | STMTval(res) = stmt; 939 | CAMLreturn(res); 940 | } 941 | 942 | EXTERNAL value 943 | caml_mysql_stmt_close(value v_stmt) 944 | { 945 | CAMLparam1(v_stmt); 946 | MYSQL_STMT* stmt = STMTval(v_stmt); 947 | check_stmt(stmt,"close"); 948 | caml_enter_blocking_section(); 949 | mysql_stmt_close(stmt); 950 | caml_leave_blocking_section(); 951 | STMTval(v_stmt) = (MYSQL_STMT*)NULL; 952 | /* 953 | * there is nothing we can do when connection is lost 954 | * and anyway in this case the stmt is automatically released by the server 955 | * so do not raise exception 956 | * 957 | * if (ret) 958 | * mysqlfailmsg("mysql_stmt_close: %s", mysql_stmt_error(stmt)); 959 | */ 960 | CAMLreturn(Val_unit); 961 | } 962 | 963 | typedef struct row_t_tag 964 | { 965 | size_t count; 966 | MYSQL_STMT* stmt; /* not owned */ 967 | 968 | MYSQL_BIND* bind; 969 | unsigned long* length; 970 | my_bool* error; 971 | my_bool* is_null; 972 | } row_t; 973 | 974 | row_t* create_row(MYSQL_STMT* stmt, size_t count) 975 | { 976 | row_t* row = malloc(sizeof(row_t)); 977 | if (row) 978 | { 979 | row->stmt = stmt; 980 | row->count = count; 981 | row->bind = calloc(count,sizeof(MYSQL_BIND)); 982 | row->error = calloc(count,sizeof(my_bool)); 983 | row->length = calloc(count,sizeof(unsigned long)); 984 | row->is_null = calloc(count,sizeof(my_bool)); 985 | } 986 | return row; 987 | } 988 | 989 | void set_param_string(row_t *r, value v, int index) 990 | { 991 | MYSQL_BIND* bind = &r->bind[index]; 992 | size_t len = caml_string_length(v); 993 | 994 | r->length[index] = len; 995 | bind->length = &r->length[index]; 996 | bind->buffer_length = len; 997 | bind->buffer_type = MYSQL_TYPE_STRING; 998 | bind->buffer = malloc(len); 999 | memcpy(bind->buffer, String_val(v), len); 1000 | } 1001 | 1002 | void set_param_null(row_t *r, int index) 1003 | { 1004 | MYSQL_BIND* bind = &r->bind[index]; 1005 | 1006 | bind->buffer_type = MYSQL_TYPE_NULL; 1007 | bind->buffer = NULL; 1008 | } 1009 | 1010 | void bind_result(row_t* r, int index) 1011 | { 1012 | MYSQL_BIND* bind = &r->bind[index]; 1013 | 1014 | bind->buffer_type = MYSQL_TYPE_STRING; 1015 | bind->buffer = 0; 1016 | bind->buffer_length = 0; 1017 | bind->is_null = &r->is_null[index]; 1018 | bind->length = &r->length[index]; 1019 | bind->error = &r->error[index]; 1020 | } 1021 | 1022 | value get_column(row_t* r, int index) 1023 | { 1024 | CAMLparam0(); 1025 | CAMLlocal1(str); 1026 | unsigned long length = r->length[index]; 1027 | MYSQL_BIND* bind = &r->bind[index]; 1028 | 1029 | if (*bind->is_null) CAMLreturn(Val_none); 1030 | if (0 == length) 1031 | { 1032 | str = caml_copy_string(""); 1033 | } 1034 | else 1035 | { 1036 | str = caml_alloc_string(length); 1037 | bind->buffer = String_val(str); 1038 | bind->buffer_length = length; 1039 | mysql_stmt_fetch_column(r->stmt, bind, index, 0); 1040 | bind->buffer = 0; /* reset binding */ 1041 | bind->buffer_length = 0; 1042 | } 1043 | 1044 | CAMLreturn(Val_some(str)); 1045 | } 1046 | 1047 | void destroy_row(row_t* r) 1048 | { 1049 | if (r) 1050 | { 1051 | free(r->bind); 1052 | free(r->error); 1053 | free(r->length); 1054 | free(r->is_null); 1055 | free(r); 1056 | } 1057 | } 1058 | 1059 | static void 1060 | stmt_result_finalize(value result) 1061 | { 1062 | row_t *row = ROWval(result); 1063 | destroy_row(row); 1064 | } 1065 | 1066 | struct custom_operations stmt_result_ops = { 1067 | "Mysql Prepared Statement Results", 1068 | stmt_result_finalize, 1069 | custom_compare_default, 1070 | custom_hash_default, 1071 | custom_serialize_default, 1072 | custom_deserialize_default, 1073 | #if defined(custom_compare_ext_default) 1074 | custom_compare_ext_default, 1075 | #endif 1076 | }; 1077 | 1078 | value 1079 | caml_mysql_stmt_execute_gen(value v_stmt, value v_params, int with_null) 1080 | { 1081 | CAMLparam2(v_stmt,v_params); 1082 | CAMLlocal2(res,v); 1083 | unsigned int i = 0; 1084 | unsigned int len = Wosize_val(v_params); 1085 | int err = 0; 1086 | row_t* row = NULL; 1087 | MYSQL_STMT* stmt = STMTval(v_stmt); 1088 | check_stmt(stmt,"execute"); 1089 | if (len != mysql_stmt_param_count(stmt)) 1090 | mysqlfailmsg("Prepared.execute : Got %i parameters, but expected %i", len, mysql_stmt_param_count(stmt)); 1091 | row = create_row(stmt, len); 1092 | if (!row) 1093 | mysqlfailwith("Prepared.execute : create_row for params"); 1094 | for (i = 0; i < len; i++) 1095 | { 1096 | v = Field(v_params,i); 1097 | if (with_null) 1098 | if (Val_none == v) 1099 | set_param_null(row, i); 1100 | else 1101 | set_param_string(row, Some_val(v), i); 1102 | else 1103 | set_param_string(row, v, i); 1104 | } 1105 | err = mysql_stmt_bind_param(stmt, row->bind); 1106 | if (err) 1107 | { 1108 | for (i = 0; i < len; i++) free(row->bind[i].buffer); 1109 | destroy_row(row); 1110 | mysqlfailmsg("Prepared.execute : mysql_stmt_bind_param = %i",err); 1111 | } 1112 | caml_enter_blocking_section(); 1113 | err = mysql_stmt_execute(stmt); 1114 | caml_leave_blocking_section(); 1115 | 1116 | for (i = 0; i < len; i++) free(row->bind[i].buffer); 1117 | destroy_row(row); 1118 | 1119 | if (err) 1120 | { 1121 | mysqlfailmsg("Prepared.execute : mysql_stmt_execute = %i, %s",err,mysql_stmt_error(stmt)); 1122 | } 1123 | 1124 | len = mysql_stmt_field_count(stmt); 1125 | row = create_row(stmt, len); 1126 | if (!row) 1127 | mysqlfailwith("Prepared.execute : create_row for results"); 1128 | if (len) 1129 | { 1130 | for (i = 0; i < len; i++) 1131 | { 1132 | bind_result(row,i); 1133 | } 1134 | if (mysql_stmt_bind_result(stmt, row->bind)) 1135 | { 1136 | destroy_row(row); 1137 | mysqlfailwith("Prepared.execute : mysql_stmt_bind_result"); 1138 | } 1139 | } 1140 | res = caml_alloc_custom(&stmt_result_ops, sizeof(row_t*), 0, 1); 1141 | ROWval(res) = row; 1142 | CAMLreturn(res); 1143 | } 1144 | 1145 | EXTERNAL value caml_mysql_stmt_execute(value v_stmt, value v_param) 1146 | { 1147 | return caml_mysql_stmt_execute_gen(v_stmt, v_param, 0); 1148 | } 1149 | 1150 | EXTERNAL value caml_mysql_stmt_execute_null(value v_stmt, value v_param) 1151 | { 1152 | return caml_mysql_stmt_execute_gen(v_stmt, v_param, 1); 1153 | } 1154 | 1155 | EXTERNAL value 1156 | caml_mysql_stmt_fetch(value result) 1157 | { 1158 | CAMLparam1(result); 1159 | CAMLlocal1(arr); 1160 | unsigned int i = 0; 1161 | int res = 0; 1162 | row_t* r = ROWval(result); 1163 | check_stmt(r->stmt,"fetch"); 1164 | caml_enter_blocking_section(); 1165 | res = mysql_stmt_fetch(r->stmt); 1166 | caml_leave_blocking_section(); 1167 | if (0 != res && MYSQL_DATA_TRUNCATED != res) CAMLreturn(Val_none); 1168 | arr = caml_alloc(r->count,0); 1169 | for (i = 0; i < r->count; i++) 1170 | { 1171 | Store_field(arr,i,get_column(r,i)); 1172 | } 1173 | CAMLreturn(Val_some(arr)); 1174 | } 1175 | 1176 | EXTERNAL value 1177 | caml_mysql_stmt_affected(value stmt) 1178 | { 1179 | CAMLparam1(stmt); 1180 | check_stmt(STMTval(stmt),"affected"); 1181 | CAMLreturn(caml_copy_int64(mysql_stmt_affected_rows(STMTval(stmt)))); 1182 | } 1183 | 1184 | EXTERNAL value 1185 | caml_mysql_stmt_insert_id(value stmt) 1186 | { 1187 | CAMLparam1(stmt); 1188 | check_stmt(STMTval(stmt),"insert_id"); 1189 | CAMLreturn(caml_copy_int64(mysql_stmt_insert_id(STMTval(stmt)))); 1190 | } 1191 | 1192 | EXTERNAL value 1193 | caml_mysql_stmt_status(value stmt) 1194 | { 1195 | CAMLparam1(stmt); 1196 | check_stmt(STMTval(stmt), "status"); 1197 | CAMLreturn(Val_int(mysql_stmt_errno(STMTval(stmt)))); 1198 | } 1199 | 1200 | EXTERNAL value 1201 | caml_mysql_stmt_result_metadata(value stmt) 1202 | { 1203 | CAMLparam1(stmt); 1204 | CAMLlocal1(res); 1205 | 1206 | check_stmt(STMTval(stmt), "result_metadata"); 1207 | res = caml_alloc_custom(&res_ops, sizeof(MYSQL_RES*), 0, 1); 1208 | RESval(res) = mysql_stmt_result_metadata(STMTval(stmt)); 1209 | 1210 | CAMLreturn(res); 1211 | } 1212 | -------------------------------------------------------------------------------- /OCamlMakefile: -------------------------------------------------------------------------------- 1 | ########################################################################### 2 | # OCamlMakefile 3 | # Copyright (C) 1999- Markus Mottl 4 | # 5 | # For updates see: 6 | # http://www.ocaml.info/home/ocaml_sources.html 7 | # 8 | ########################################################################### 9 | 10 | # Modified by damien for .glade.ml compilation 11 | # Modified by ygrek: do not pass OCAMLFIND_INSTFLAGS to `ocamlfind remove` 12 | 13 | # Set these variables to the names of the sources to be processed and 14 | # the result variable. Order matters during linkage! 15 | 16 | ifndef SOURCES 17 | SOURCES := foo.ml 18 | endif 19 | export SOURCES 20 | 21 | ifndef RES_CLIB_SUF 22 | RES_CLIB_SUF := _stubs 23 | endif 24 | export RES_CLIB_SUF 25 | 26 | ifndef RESULT 27 | RESULT := foo 28 | endif 29 | export RESULT := $(strip $(RESULT)) 30 | 31 | export LIB_PACK_NAME 32 | 33 | ifndef DOC_FILES 34 | DOC_FILES := $(filter %.mli, $(SOURCES)) 35 | endif 36 | export DOC_FILES 37 | FIRST_DOC_FILE := $(firstword $(DOC_FILES)) 38 | 39 | export BCSUFFIX 40 | export NCSUFFIX 41 | 42 | ifndef TOPSUFFIX 43 | TOPSUFFIX := .top 44 | endif 45 | export TOPSUFFIX 46 | 47 | # Eventually set include- and library-paths, libraries to link, 48 | # additional compilation-, link- and ocamlyacc-flags 49 | # Path- and library information needs not be written with "-I" and such... 50 | # Define THREADS if you need it, otherwise leave it unset (same for 51 | # USE_CAMLP4)! 52 | 53 | export THREADS 54 | export VMTHREADS 55 | export ANNOTATE 56 | export USE_CAMLP4 57 | 58 | export INCDIRS 59 | export LIBDIRS 60 | export EXTLIBDIRS 61 | export RESULTDEPS 62 | export OCAML_DEFAULT_DIRS 63 | 64 | export LIBS 65 | export CLIBS 66 | export CFRAMEWORKS 67 | 68 | export OCAMLFLAGS 69 | export OCAMLNCFLAGS 70 | export OCAMLBCFLAGS 71 | 72 | export OCAMLLDFLAGS 73 | export OCAMLNLDFLAGS 74 | export OCAMLBLDFLAGS 75 | 76 | export OCAMLMKLIB_FLAGS 77 | 78 | ifndef OCAMLCPFLAGS 79 | OCAMLCPFLAGS := a 80 | endif 81 | export OCAMLCPFLAGS 82 | 83 | ifndef DOC_DIR 84 | DOC_DIR := doc 85 | endif 86 | export DOC_DIR 87 | 88 | export PPFLAGS 89 | 90 | export LFLAGS 91 | export YFLAGS 92 | export IDLFLAGS 93 | 94 | export OCAMLDOCFLAGS 95 | 96 | export OCAMLFIND_INSTFLAGS 97 | 98 | export DVIPSFLAGS 99 | 100 | export STATIC 101 | 102 | # Add a list of optional trash files that should be deleted by "make clean" 103 | export TRASH 104 | 105 | ECHO := echo 106 | 107 | ifdef REALLY_QUIET 108 | export REALLY_QUIET 109 | ECHO := true 110 | LFLAGS := $(LFLAGS) -q 111 | YFLAGS := $(YFLAGS) -q 112 | endif 113 | 114 | #################### variables depending on your OCaml-installation 115 | 116 | SYSTEM := $(shell ocamlc -config 2>/dev/null | grep system | sed 's/system: //') 117 | # This may be 118 | # - mingw 119 | # - win32 120 | # - cygwin 121 | # - some other string means Unix 122 | # - empty means ocamlc does not support -config 123 | 124 | ifeq ($(SYSTEM),mingw) 125 | MINGW=1 126 | endif 127 | ifeq ($(SYSTEM),win32) 128 | MSVC=1 129 | endif 130 | 131 | ifdef MINGW 132 | export MINGW 133 | WIN32 := 1 134 | # We are compiling with cygwin tools: 135 | CFLAGS_WIN32 := -mno-cygwin 136 | # The default value 'cc' makes 'ocamlc -cc "cc"' raises the error 'The 137 | # NTVDM CPU has encountered an illegal instruction'. 138 | CC := gcc 139 | # The OCaml C header files use this flag: 140 | CFLAGS += -D__MINGW32__ 141 | endif 142 | ifdef MSVC 143 | export MSVC 144 | WIN32 := 1 145 | ifndef STATIC 146 | CPPFLAGS_WIN32 := -DCAML_DLL 147 | endif 148 | CFLAGS_WIN32 += -nologo 149 | EXT_OBJ := obj 150 | EXT_LIB := lib 151 | ifeq ($(CC),gcc) 152 | # work around GNU Make default value 153 | ifdef THREADS 154 | CC := cl -MT 155 | else 156 | CC := cl 157 | endif 158 | endif 159 | ifeq ($(CXX),g++) 160 | # work around GNU Make default value 161 | CXX := $(CC) 162 | endif 163 | CFLAG_O := -Fo 164 | endif 165 | ifdef WIN32 166 | EXT_CXX := cpp 167 | EXE := .exe 168 | endif 169 | 170 | ifndef EXT_OBJ 171 | EXT_OBJ := o 172 | endif 173 | ifndef EXT_LIB 174 | EXT_LIB := a 175 | endif 176 | ifndef EXT_CXX 177 | EXT_CXX := cc 178 | endif 179 | ifndef EXE 180 | EXE := # empty 181 | endif 182 | ifndef CFLAG_O 183 | CFLAG_O := -o # do not delete this comment (preserves trailing whitespace)! 184 | endif 185 | 186 | export CC 187 | export CXX 188 | export CFLAGS 189 | export CXXFLAGS 190 | export LDFLAGS 191 | export CPPFLAGS 192 | 193 | ifndef RPATH_FLAG 194 | ifdef ELF_RPATH_FLAG 195 | RPATH_FLAG := $(ELF_RPATH_FLAG) 196 | else 197 | RPATH_FLAG := -R 198 | endif 199 | endif 200 | export RPATH_FLAG 201 | 202 | ifndef MSVC 203 | ifndef PIC_CFLAGS 204 | PIC_CFLAGS := -fPIC 205 | endif 206 | ifndef PIC_CPPFLAGS 207 | PIC_CPPFLAGS := -DPIC 208 | endif 209 | endif 210 | 211 | export PIC_CFLAGS 212 | export PIC_CPPFLAGS 213 | 214 | BCRESULT := $(addsuffix $(BCSUFFIX), $(RESULT)) 215 | NCRESULT := $(addsuffix $(NCSUFFIX), $(RESULT)) 216 | TOPRESULT := $(addsuffix $(TOPSUFFIX), $(RESULT)) 217 | 218 | ifndef OCAMLFIND 219 | OCAMLFIND := ocamlfind 220 | endif 221 | export OCAMLFIND 222 | 223 | ifndef OCAMLC 224 | OCAMLC := ocamlc 225 | endif 226 | export OCAMLC 227 | 228 | ifndef OCAMLOPT 229 | OCAMLOPT := ocamlopt 230 | endif 231 | export OCAMLOPT 232 | 233 | ifndef OCAMLMKTOP 234 | OCAMLMKTOP := ocamlmktop 235 | endif 236 | export OCAMLMKTOP 237 | 238 | ifndef OCAMLCP 239 | OCAMLCP := ocamlcp 240 | endif 241 | export OCAMLCP 242 | 243 | ifndef OCAMLDEP 244 | OCAMLDEP := ocamldep 245 | endif 246 | export OCAMLDEP 247 | 248 | ifndef OCAMLLEX 249 | OCAMLLEX := ocamllex 250 | endif 251 | export OCAMLLEX 252 | 253 | ifndef OCAMLYACC 254 | OCAMLYACC := ocamlyacc 255 | endif 256 | export OCAMLYACC 257 | 258 | ifndef OCAMLMKLIB 259 | OCAMLMKLIB := ocamlmklib 260 | endif 261 | export OCAMLMKLIB 262 | 263 | ifndef OCAML_GLADECC 264 | OCAML_GLADECC := lablgladecc2 265 | endif 266 | export OCAML_GLADECC 267 | 268 | ifndef OCAML_GLADECC_FLAGS 269 | OCAML_GLADECC_FLAGS := 270 | endif 271 | export OCAML_GLADECC_FLAGS 272 | 273 | ifndef CAMELEON_REPORT 274 | CAMELEON_REPORT := report 275 | endif 276 | export CAMELEON_REPORT 277 | 278 | ifndef CAMELEON_REPORT_FLAGS 279 | CAMELEON_REPORT_FLAGS := 280 | endif 281 | export CAMELEON_REPORT_FLAGS 282 | 283 | ifndef CAMELEON_ZOGGY 284 | CAMELEON_ZOGGY := camlp4o pa_zog.cma pr_o.cmo 285 | endif 286 | export CAMELEON_ZOGGY 287 | 288 | ifndef CAMELEON_ZOGGY_FLAGS 289 | CAMELEON_ZOGGY_FLAGS := 290 | endif 291 | export CAMELEON_ZOGGY_FLAGS 292 | 293 | ifndef OXRIDL 294 | OXRIDL := oxridl 295 | endif 296 | export OXRIDL 297 | 298 | ifndef CAMLIDL 299 | CAMLIDL := camlidl 300 | endif 301 | export CAMLIDL 302 | 303 | ifndef CAMLIDLDLL 304 | CAMLIDLDLL := camlidldll 305 | endif 306 | export CAMLIDLDLL 307 | 308 | ifndef NOIDLHEADER 309 | MAYBE_IDL_HEADER := -header 310 | endif 311 | export NOIDLHEADER 312 | 313 | export NO_CUSTOM 314 | 315 | ifndef CAMLP4 316 | CAMLP4 := camlp4 317 | endif 318 | export CAMLP4 319 | 320 | ifndef REAL_OCAMLFIND 321 | ifdef PACKS 322 | ifndef CREATE_LIB 323 | ifdef THREADS 324 | PACKS += threads 325 | endif 326 | endif 327 | empty := 328 | space := $(empty) $(empty) 329 | comma := , 330 | ifdef PREDS 331 | PRE_OCAML_FIND_PREDICATES := $(subst $(space),$(comma),$(PREDS)) 332 | PRE_OCAML_FIND_PACKAGES := $(subst $(space),$(comma),$(PACKS)) 333 | OCAML_FIND_PREDICATES := -predicates $(PRE_OCAML_FIND_PREDICATES) 334 | # OCAML_DEP_PREDICATES := -syntax $(PRE_OCAML_FIND_PREDICATES) 335 | OCAML_FIND_PACKAGES := $(OCAML_FIND_PREDICATES) -package $(PRE_OCAML_FIND_PACKAGES) 336 | OCAML_DEP_PACKAGES := $(OCAML_DEP_PREDICATES) -package $(PRE_OCAML_FIND_PACKAGES) 337 | else 338 | OCAML_FIND_PACKAGES := -package $(subst $(space),$(comma),$(PACKS)) 339 | OCAML_DEP_PACKAGES := 340 | endif 341 | OCAML_FIND_LINKPKG := -linkpkg 342 | REAL_OCAMLFIND := $(OCAMLFIND) 343 | endif 344 | endif 345 | 346 | export OCAML_FIND_PACKAGES 347 | export OCAML_DEP_PACKAGES 348 | export OCAML_FIND_LINKPKG 349 | export REAL_OCAMLFIND 350 | 351 | ifndef OCAMLDOC 352 | OCAMLDOC := ocamldoc 353 | endif 354 | export OCAMLDOC 355 | 356 | ifndef LATEX 357 | LATEX := latex 358 | endif 359 | export LATEX 360 | 361 | ifndef DVIPS 362 | DVIPS := dvips 363 | endif 364 | export DVIPS 365 | 366 | ifndef PS2PDF 367 | PS2PDF := ps2pdf 368 | endif 369 | export PS2PDF 370 | 371 | ifndef OCAMLMAKEFILE 372 | OCAMLMAKEFILE := OCamlMakefile 373 | endif 374 | export OCAMLMAKEFILE 375 | 376 | ifndef OCAMLLIBPATH 377 | OCAMLLIBPATH := \ 378 | $(shell $(OCAMLC) 2>/dev/null -where || echo /usr/local/lib/ocaml) 379 | endif 380 | export OCAMLLIBPATH 381 | 382 | ifndef OCAML_LIB_INSTALL 383 | OCAML_LIB_INSTALL := $(OCAMLLIBPATH)/contrib 384 | endif 385 | export OCAML_LIB_INSTALL 386 | 387 | ########################################################################### 388 | 389 | #################### change following sections only if 390 | #################### you know what you are doing! 391 | 392 | # delete target files when a build command fails 393 | .PHONY: .DELETE_ON_ERROR 394 | .DELETE_ON_ERROR: 395 | 396 | # for pedants using "--warn-undefined-variables" 397 | export MAYBE_IDL 398 | export REAL_RESULT 399 | export CAMLIDLFLAGS 400 | export THREAD_FLAG 401 | export RES_CLIB 402 | export MAKEDLL 403 | export ANNOT_FLAG 404 | export C_OXRIDL 405 | export SUBPROJS 406 | export CFLAGS_WIN32 407 | export CPPFLAGS_WIN32 408 | 409 | INCFLAGS := 410 | 411 | SHELL := /bin/sh 412 | 413 | MLDEPDIR := ._d 414 | BCDIDIR := ._bcdi 415 | NCDIDIR := ._ncdi 416 | 417 | FILTER_EXTNS := %.mli %.ml %.mll %.mly %.idl %.oxridl %.c %.m %.$(EXT_CXX) %.rep %.zog %.glade 418 | 419 | FILTERED := $(filter $(FILTER_EXTNS), $(SOURCES)) 420 | SOURCE_DIRS := $(filter-out ./, $(sort $(dir $(FILTERED)))) 421 | 422 | FILTERED_REP := $(filter %.rep, $(FILTERED)) 423 | DEP_REP := $(FILTERED_REP:%.rep=$(MLDEPDIR)/%.d) 424 | AUTO_REP := $(FILTERED_REP:.rep=.ml) 425 | 426 | FILTERED_ZOG := $(filter %.zog, $(FILTERED)) 427 | DEP_ZOG := $(FILTERED_ZOG:%.zog=$(MLDEPDIR)/%.d) 428 | AUTO_ZOG := $(FILTERED_ZOG:.zog=.ml) 429 | 430 | FILTERED_GLADE := $(filter %.glade, $(FILTERED)) 431 | DEP_GLADE := $(FILTERED_GLADE:%.glade=$(MLDEPDIR)/%.d) 432 | AUTO_GLADE := $(FILTERED_GLADE:.glade=.ml) 433 | 434 | FILTERED_ML := $(filter %.ml, $(FILTERED)) 435 | DEP_ML := $(FILTERED_ML:%.ml=$(MLDEPDIR)/%.d) 436 | 437 | FILTERED_MLI := $(filter %.mli, $(FILTERED)) 438 | DEP_MLI := $(FILTERED_MLI:.mli=.di) 439 | 440 | FILTERED_MLL := $(filter %.mll, $(FILTERED)) 441 | DEP_MLL := $(FILTERED_MLL:%.mll=$(MLDEPDIR)/%.d) 442 | AUTO_MLL := $(FILTERED_MLL:.mll=.ml) 443 | 444 | FILTERED_MLY := $(filter %.mly, $(FILTERED)) 445 | DEP_MLY := $(FILTERED_MLY:%.mly=$(MLDEPDIR)/%.d) $(FILTERED_MLY:.mly=.di) 446 | AUTO_MLY := $(FILTERED_MLY:.mly=.mli) $(FILTERED_MLY:.mly=.ml) 447 | 448 | FILTERED_IDL := $(filter %.idl, $(FILTERED)) 449 | DEP_IDL := $(FILTERED_IDL:%.idl=$(MLDEPDIR)/%.d) $(FILTERED_IDL:.idl=.di) 450 | C_IDL := $(FILTERED_IDL:%.idl=%_stubs.c) 451 | ifndef NOIDLHEADER 452 | C_IDL += $(FILTERED_IDL:.idl=.h) 453 | endif 454 | OBJ_C_IDL := $(FILTERED_IDL:%.idl=%_stubs.$(EXT_OBJ)) 455 | AUTO_IDL := $(FILTERED_IDL:.idl=.mli) $(FILTERED_IDL:.idl=.ml) $(C_IDL) 456 | 457 | FILTERED_OXRIDL := $(filter %.oxridl, $(FILTERED)) 458 | DEP_OXRIDL := $(FILTERED_OXRIDL:%.oxridl=$(MLDEPDIR)/%.d) $(FILTERED_OXRIDL:.oxridl=.di) 459 | AUTO_OXRIDL := $(FILTERED_OXRIDL:.oxridl=.mli) $(FILTERED_OXRIDL:.oxridl=.ml) $(C_OXRIDL) 460 | 461 | FILTERED_C_CXX := $(filter %.c %.m %.$(EXT_CXX), $(FILTERED)) 462 | OBJ_C_CXX := $(FILTERED_C_CXX:.c=.$(EXT_OBJ)) 463 | OBJ_C_CXX := $(OBJ_C_CXX:.m=.$(EXT_OBJ)) 464 | OBJ_C_CXX := $(OBJ_C_CXX:.$(EXT_CXX)=.$(EXT_OBJ)) 465 | 466 | PRE_TARGETS += $(AUTO_MLL) $(AUTO_MLY) $(AUTO_IDL) $(AUTO_OXRIDL) $(AUTO_ZOG) $(AUTO_REP) $(AUTO_GLADE) 467 | 468 | ALL_DEPS := $(DEP_ML) $(DEP_MLI) $(DEP_MLL) $(DEP_MLY) $(DEP_IDL) $(DEP_OXRIDL) $(DEP_ZOG) $(DEP_REP) $(DEP_GLADE) 469 | 470 | MLDEPS := $(filter %.d, $(ALL_DEPS)) 471 | MLIDEPS := $(filter %.di, $(ALL_DEPS)) 472 | BCDEPIS := $(MLIDEPS:%.di=$(BCDIDIR)/%.di) 473 | NCDEPIS := $(MLIDEPS:%.di=$(NCDIDIR)/%.di) 474 | 475 | ALLML := $(filter %.mli %.ml %.mll %.mly %.idl %.oxridl %.rep %.zog %.glade, $(FILTERED)) 476 | 477 | IMPLO_INTF := $(ALLML:%.mli=%.mli.__) 478 | IMPLO_INTF := $(foreach file, $(IMPLO_INTF), \ 479 | $(basename $(file)).cmi $(basename $(file)).cmo) 480 | IMPLO_INTF := $(filter-out %.mli.cmo, $(IMPLO_INTF)) 481 | IMPLO_INTF := $(IMPLO_INTF:%.mli.cmi=%.cmi) 482 | 483 | IMPLX_INTF := $(IMPLO_INTF:.cmo=.cmx) 484 | 485 | INTF := $(filter %.cmi, $(IMPLO_INTF)) 486 | IMPL_CMO := $(filter %.cmo, $(IMPLO_INTF)) 487 | IMPL_CMX := $(IMPL_CMO:.cmo=.cmx) 488 | IMPL_ASM := $(IMPL_CMO:.cmo=.asm) 489 | IMPL_S := $(IMPL_CMO:.cmo=.s) 490 | 491 | OBJ_LINK := $(OBJ_C_IDL) $(OBJ_C_CXX) 492 | OBJ_FILES := $(IMPL_CMO:.cmo=.$(EXT_OBJ)) $(OBJ_LINK) 493 | 494 | EXECS := $(addsuffix $(EXE), \ 495 | $(sort $(TOPRESULT) $(BCRESULT) $(NCRESULT))) 496 | ifdef WIN32 497 | EXECS += $(BCRESULT).dll $(NCRESULT).dll 498 | endif 499 | 500 | CLIB_BASE := $(RESULT)$(RES_CLIB_SUF) 501 | ifneq ($(strip $(OBJ_LINK)),) 502 | RES_CLIB := lib$(CLIB_BASE).$(EXT_LIB) 503 | endif 504 | 505 | ifdef WIN32 506 | DLLSONAME := dll$(CLIB_BASE).dll 507 | else 508 | DLLSONAME := dll$(CLIB_BASE).so 509 | endif 510 | 511 | NONEXECS := $(INTF) $(IMPL_CMO) $(IMPL_CMX) $(IMPL_ASM) $(IMPL_S) \ 512 | $(OBJ_FILES) $(PRE_TARGETS) $(BCRESULT).cma $(NCRESULT).cmxa \ 513 | $(NCRESULT).$(EXT_LIB) $(BCRESULT).cmi $(BCRESULT).cmo \ 514 | $(NCRESULT).cmi $(NCRESULT).cmx $(NCRESULT).$(EXT_OBJ) \ 515 | $(RES_CLIB) $(IMPL_CMO:.cmo=.annot) \ 516 | $(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmo $(LIB_PACK_NAME).cmx \ 517 | $(LIB_PACK_NAME).$(EXT_OBJ) 518 | 519 | ifndef STATIC 520 | NONEXECS += $(DLLSONAME) 521 | endif 522 | 523 | ifndef LIBINSTALL_FILES 524 | LIBINSTALL_FILES := $(RESULT).mli $(RESULT).cmi $(RESULT).cma \ 525 | $(RESULT).cmxa $(RESULT).$(EXT_LIB) $(RES_CLIB) 526 | ifndef STATIC 527 | ifneq ($(strip $(OBJ_LINK)),) 528 | LIBINSTALL_FILES += $(DLLSONAME) 529 | endif 530 | endif 531 | endif 532 | 533 | export LIBINSTALL_FILES 534 | 535 | ifdef WIN32 536 | # some extra stuff is created while linking DLLs 537 | NONEXECS += $(BCRESULT).$(EXT_LIB) $(BCRESULT).exp $(NCRESULT).exp $(CLIB_BASE).exp $(CLIB_BASE).lib 538 | endif 539 | 540 | TARGETS := $(EXECS) $(NONEXECS) 541 | 542 | # If there are IDL-files 543 | ifneq ($(strip $(FILTERED_IDL)),) 544 | MAYBE_IDL := -cclib -lcamlidl 545 | endif 546 | 547 | ifdef USE_CAMLP4 548 | CAMLP4PATH := \ 549 | $(shell $(CAMLP4) -where 2>/dev/null || echo /usr/local/lib/camlp4) 550 | INCFLAGS := -I $(CAMLP4PATH) 551 | CINCFLAGS := -I$(CAMLP4PATH) 552 | endif 553 | 554 | DINCFLAGS := $(INCFLAGS) $(SOURCE_DIRS:%=-I %) $(OCAML_DEFAULT_DIRS:%=-I %) 555 | INCFLAGS := $(DINCFLAGS) $(INCDIRS:%=-I %) 556 | CINCFLAGS += $(SOURCE_DIRS:%=-I%) $(INCDIRS:%=-I%) $(OCAML_DEFAULT_DIRS:%=-I%) 557 | 558 | ifndef MSVC 559 | CLIBFLAGS += $(SOURCE_DIRS:%=-L%) $(LIBDIRS:%=-L%) \ 560 | $(EXTLIBDIRS:%=-L%) $(OCAML_DEFAULT_DIRS:%=-L%) 561 | 562 | ifeq ($(ELF_RPATH), yes) 563 | CLIBFLAGS += $(EXTLIBDIRS:%=-Wl,$(RPATH_FLAG)%) 564 | endif 565 | endif 566 | 567 | ifndef PROFILING 568 | INTF_OCAMLC := $(OCAMLC) 569 | else 570 | ifndef THREADS 571 | INTF_OCAMLC := $(OCAMLCP) -p $(OCAMLCPFLAGS) 572 | else 573 | # OCaml does not support profiling byte code 574 | # with threads (yet), therefore we force an error. 575 | ifndef REAL_OCAMLC 576 | $(error Profiling of multithreaded byte code not yet supported by OCaml) 577 | endif 578 | INTF_OCAMLC := $(OCAMLC) 579 | endif 580 | endif 581 | 582 | ifndef MSVC 583 | COMMON_LDFLAGS := $(LDFLAGS:%=-ccopt %) $(SOURCE_DIRS:%=-ccopt -L%) \ 584 | $(LIBDIRS:%=-ccopt -L%) $(EXTLIBDIRS:%=-ccopt -L%) \ 585 | $(EXTLIBDIRS:%=-ccopt -Wl $(OCAML_DEFAULT_DIRS:%=-ccopt -L%)) 586 | 587 | ifeq ($(ELF_RPATH),yes) 588 | COMMON_LDFLAGS += $(EXTLIBDIRS:%=-ccopt -Wl,$(RPATH_FLAG)%) 589 | endif 590 | else 591 | COMMON_LDFLAGS := -ccopt "/link -NODEFAULTLIB:LIBC $(LDFLAGS:%=%) $(SOURCE_DIRS:%=-LIBPATH:%) \ 592 | $(LIBDIRS:%=-LIBPATH:%) $(EXTLIBDIRS:%=-LIBPATH:%) \ 593 | $(OCAML_DEFAULT_DIRS:%=-LIBPATH:%) " 594 | endif 595 | 596 | CLIBS_OPTS := $(CLIBS:%=-cclib -l%) $(CFRAMEWORKS:%=-cclib '-framework %') 597 | ifdef MSVC 598 | ifndef STATIC 599 | # MSVC libraries do not have 'lib' prefix 600 | CLIBS_OPTS := $(CLIBS:%=-cclib %.lib) 601 | endif 602 | endif 603 | 604 | ifneq ($(strip $(OBJ_LINK)),) 605 | ifdef CREATE_LIB 606 | OBJS_LIBS := -cclib -l$(CLIB_BASE) $(CLIBS_OPTS) $(MAYBE_IDL) 607 | else 608 | OBJS_LIBS := $(OBJ_LINK) $(CLIBS_OPTS) $(MAYBE_IDL) 609 | endif 610 | else 611 | OBJS_LIBS := $(CLIBS_OPTS) $(MAYBE_IDL) 612 | endif 613 | 614 | ifdef LIB_PACK_NAME 615 | FOR_PACK_NAME := $(shell echo $(LIB_PACK_NAME) | awk '{print toupper(substr($$0,1,1))substr($$0,2)}') 616 | endif 617 | 618 | # If we have to make byte-code 619 | ifndef REAL_OCAMLC 620 | BYTE_OCAML := y 621 | 622 | # EXTRADEPS is added dependencies we have to insert for all 623 | # executable files we generate. Ideally it should be all of the 624 | # libraries we use, but it's hard to find the ones that get searched on 625 | # the path since I don't know the paths built into the compiler, so 626 | # just include the ones with slashes in their names. 627 | EXTRADEPS := $(addsuffix .cma,$(foreach i,$(LIBS),$(if $(findstring /,$(i)),$(i)))) 628 | 629 | 630 | ifndef LIB_PACK_NAME 631 | SPECIAL_OCAMLFLAGS := $(OCAMLBCFLAGS) 632 | else 633 | SPECIAL_OCAMLFLAGS := -for-pack $(FOR_PACK_NAME) $(OCAMLBCFLAGS) 634 | endif 635 | 636 | REAL_OCAMLC := $(INTF_OCAMLC) 637 | 638 | REAL_IMPL := $(IMPL_CMO) 639 | REAL_IMPL_INTF := $(IMPLO_INTF) 640 | IMPL_SUF := .cmo 641 | 642 | DEPFLAGS := 643 | MAKE_DEPS := $(MLDEPS) $(BCDEPIS) 644 | 645 | ifdef CREATE_LIB 646 | override CFLAGS := $(PIC_CFLAGS) $(CFLAGS) 647 | override CPPFLAGS := $(PIC_CPPFLAGS) $(CPPFLAGS) 648 | ifndef STATIC 649 | ifneq ($(strip $(OBJ_LINK)),) 650 | MAKEDLL := $(DLLSONAME) 651 | ALL_LDFLAGS := -dllib $(DLLSONAME) 652 | endif 653 | endif 654 | endif 655 | 656 | ifndef NO_CUSTOM 657 | ifneq "$(strip $(OBJ_LINK) $(THREADS) $(MAYBE_IDL) $(CLIBS) $(CFRAMEWORKS))" "" 658 | ALL_LDFLAGS += -custom 659 | endif 660 | endif 661 | 662 | ALL_LDFLAGS += $(INCFLAGS) $(OCAMLLDFLAGS) $(OCAMLBLDFLAGS) \ 663 | $(COMMON_LDFLAGS) $(LIBS:%=%.cma) 664 | CAMLIDLDLLFLAGS := 665 | 666 | ifdef THREADS 667 | ifdef VMTHREADS 668 | THREAD_FLAG := -vmthread 669 | else 670 | THREAD_FLAG := -thread 671 | endif 672 | ALL_LDFLAGS := $(THREAD_FLAG) $(ALL_LDFLAGS) 673 | ifndef CREATE_LIB 674 | ifndef REAL_OCAMLFIND 675 | ALL_LDFLAGS := unix.cma threads.cma $(ALL_LDFLAGS) 676 | endif 677 | endif 678 | endif 679 | 680 | # we have to make native-code 681 | else 682 | EXTRADEPS := $(addsuffix .cmxa,$(foreach i,$(LIBS),$(if $(findstring /,$(i)),$(i)))) 683 | ifndef PROFILING 684 | SPECIAL_OCAMLFLAGS := $(OCAMLNCFLAGS) 685 | PLDFLAGS := 686 | else 687 | SPECIAL_OCAMLFLAGS := -p $(OCAMLNCFLAGS) 688 | PLDFLAGS := -p 689 | endif 690 | 691 | ifndef LIB_PACK_NAME 692 | SPECIAL_OCAMLFLAGS := $(OCAMLNCFLAGS) 693 | else 694 | SPECIAL_OCAMLFLAGS := -for-pack $(FOR_PACK_NAME) $(OCAMLNCFLAGS) 695 | endif 696 | REAL_IMPL := $(IMPL_CMX) 697 | REAL_IMPL_INTF := $(IMPLX_INTF) 698 | IMPL_SUF := .cmx 699 | 700 | override CPPFLAGS := -DNATIVE_CODE $(CPPFLAGS) 701 | 702 | DEPFLAGS := -native 703 | MAKE_DEPS := $(MLDEPS) $(NCDEPIS) 704 | 705 | ALL_LDFLAGS := $(PLDFLAGS) $(INCFLAGS) $(OCAMLLDFLAGS) \ 706 | $(OCAMLNLDFLAGS) $(COMMON_LDFLAGS) 707 | CAMLIDLDLLFLAGS := -opt 708 | 709 | ifndef CREATE_LIB 710 | ALL_LDFLAGS += $(LIBS:%=%.cmxa) 711 | else 712 | override CFLAGS := $(PIC_CFLAGS) $(CFLAGS) 713 | override CPPFLAGS := $(PIC_CPPFLAGS) $(CPPFLAGS) 714 | endif 715 | 716 | ifdef THREADS 717 | THREAD_FLAG := -thread 718 | ALL_LDFLAGS := $(THREAD_FLAG) $(ALL_LDFLAGS) 719 | ifndef CREATE_LIB 720 | ifndef REAL_OCAMLFIND 721 | ALL_LDFLAGS := unix.cmxa threads.cmxa $(ALL_LDFLAGS) 722 | endif 723 | endif 724 | endif 725 | endif 726 | 727 | export MAKE_DEPS 728 | 729 | ifdef ANNOTATE 730 | ANNOT_FLAG := -dtypes 731 | else 732 | endif 733 | 734 | ALL_OCAMLCFLAGS := $(THREAD_FLAG) $(ANNOT_FLAG) $(OCAMLFLAGS) \ 735 | $(INCFLAGS) $(SPECIAL_OCAMLFLAGS) 736 | 737 | ifdef make_deps 738 | -include $(MAKE_DEPS) 739 | PRE_TARGETS := 740 | endif 741 | 742 | ########################################################################### 743 | # USER RULES 744 | 745 | # Call "OCamlMakefile QUIET=" to get rid of all of the @'s. 746 | QUIET=@ 747 | 748 | # generates byte-code (default) 749 | byte-code: $(PRE_TARGETS) 750 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(BCRESULT) \ 751 | REAL_RESULT="$(BCRESULT)" make_deps=yes 752 | bc: byte-code 753 | 754 | byte-code-nolink: $(PRE_TARGETS) 755 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) nolink \ 756 | REAL_RESULT="$(BCRESULT)" make_deps=yes 757 | bcnl: byte-code-nolink 758 | 759 | top: $(PRE_TARGETS) 760 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(TOPRESULT) \ 761 | REAL_RESULT="$(BCRESULT)" make_deps=yes 762 | 763 | # generates native-code 764 | 765 | native-code: $(PRE_TARGETS) 766 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(NCRESULT) \ 767 | REAL_RESULT="$(NCRESULT)" \ 768 | REAL_OCAMLC="$(OCAMLOPT)" \ 769 | make_deps=yes 770 | nc: native-code 771 | 772 | native-code-nolink: $(PRE_TARGETS) 773 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) nolink \ 774 | REAL_RESULT="$(NCRESULT)" \ 775 | REAL_OCAMLC="$(OCAMLOPT)" \ 776 | make_deps=yes 777 | ncnl: native-code-nolink 778 | 779 | # generates byte-code libraries 780 | byte-code-library: $(PRE_TARGETS) 781 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ 782 | $(RES_CLIB) $(BCRESULT).cma \ 783 | REAL_RESULT="$(BCRESULT)" \ 784 | CREATE_LIB=yes \ 785 | make_deps=yes 786 | bcl: byte-code-library 787 | 788 | # generates native-code libraries 789 | native-code-library: $(PRE_TARGETS) 790 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ 791 | $(RES_CLIB) $(NCRESULT).cmxa \ 792 | REAL_RESULT="$(NCRESULT)" \ 793 | REAL_OCAMLC="$(OCAMLOPT)" \ 794 | CREATE_LIB=yes \ 795 | make_deps=yes 796 | ncl: native-code-library 797 | 798 | ifdef WIN32 799 | # generates byte-code dll 800 | byte-code-dll: $(PRE_TARGETS) 801 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ 802 | $(RES_CLIB) $(BCRESULT).dll \ 803 | REAL_RESULT="$(BCRESULT)" \ 804 | make_deps=yes 805 | bcd: byte-code-dll 806 | 807 | # generates native-code dll 808 | native-code-dll: $(PRE_TARGETS) 809 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ 810 | $(RES_CLIB) $(NCRESULT).dll \ 811 | REAL_RESULT="$(NCRESULT)" \ 812 | REAL_OCAMLC="$(OCAMLOPT)" \ 813 | make_deps=yes 814 | ncd: native-code-dll 815 | endif 816 | 817 | # generates byte-code with debugging information 818 | debug-code: $(PRE_TARGETS) 819 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(BCRESULT) \ 820 | REAL_RESULT="$(BCRESULT)" make_deps=yes \ 821 | OCAMLFLAGS="-g $(OCAMLFLAGS)" \ 822 | OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" 823 | dc: debug-code 824 | 825 | debug-code-nolink: $(PRE_TARGETS) 826 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) nolink \ 827 | REAL_RESULT="$(BCRESULT)" make_deps=yes \ 828 | OCAMLFLAGS="-g $(OCAMLFLAGS)" \ 829 | OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" 830 | dcnl: debug-code-nolink 831 | 832 | # generates byte-code with debugging information (native code) 833 | debug-native-code: $(PRE_TARGETS) 834 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(NCRESULT) \ 835 | REAL_RESULT="$(NCRESULT)" make_deps=yes \ 836 | REAL_OCAMLC="$(OCAMLOPT)" \ 837 | OCAMLFLAGS="-g $(OCAMLFLAGS)" \ 838 | OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" 839 | dnc: debug-native-code 840 | 841 | debug-native-code-nolink: $(PRE_TARGETS) 842 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) nolink \ 843 | REAL_RESULT="$(NCRESULT)" make_deps=yes \ 844 | REAL_OCAMLC="$(OCAMLOPT)" \ 845 | OCAMLFLAGS="-g $(OCAMLFLAGS)" \ 846 | OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" 847 | dncnl: debug-native-code-nolink 848 | 849 | # generates byte-code libraries with debugging information 850 | debug-code-library: $(PRE_TARGETS) 851 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ 852 | $(RES_CLIB) $(BCRESULT).cma \ 853 | REAL_RESULT="$(BCRESULT)" make_deps=yes \ 854 | CREATE_LIB=yes \ 855 | OCAMLFLAGS="-g $(OCAMLFLAGS)" \ 856 | OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" 857 | dcl: debug-code-library 858 | 859 | # generates byte-code libraries with debugging information (native code) 860 | debug-native-code-library: $(PRE_TARGETS) 861 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ 862 | $(RES_CLIB) $(NCRESULT).cma \ 863 | REAL_RESULT="$(NCRESULT)" make_deps=yes \ 864 | REAL_OCAMLC="$(OCAMLOPT)" \ 865 | CREATE_LIB=yes \ 866 | OCAMLFLAGS="-g $(OCAMLFLAGS)" \ 867 | OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" 868 | dncl: debug-native-code-library 869 | 870 | # generates byte-code for profiling 871 | profiling-byte-code: $(PRE_TARGETS) 872 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(BCRESULT) \ 873 | REAL_RESULT="$(BCRESULT)" PROFILING="y" \ 874 | make_deps=yes 875 | pbc: profiling-byte-code 876 | 877 | # generates native-code 878 | 879 | profiling-native-code: $(PRE_TARGETS) 880 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(NCRESULT) \ 881 | REAL_RESULT="$(NCRESULT)" \ 882 | REAL_OCAMLC="$(OCAMLOPT)" \ 883 | PROFILING="y" \ 884 | make_deps=yes 885 | pnc: profiling-native-code 886 | 887 | # generates byte-code libraries 888 | profiling-byte-code-library: $(PRE_TARGETS) 889 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ 890 | $(RES_CLIB) $(BCRESULT).cma \ 891 | REAL_RESULT="$(BCRESULT)" PROFILING="y" \ 892 | CREATE_LIB=yes \ 893 | make_deps=yes 894 | pbcl: profiling-byte-code-library 895 | 896 | # generates native-code libraries 897 | profiling-native-code-library: $(PRE_TARGETS) 898 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ 899 | $(RES_CLIB) $(NCRESULT).cmxa \ 900 | REAL_RESULT="$(NCRESULT)" PROFILING="y" \ 901 | REAL_OCAMLC="$(OCAMLOPT)" \ 902 | CREATE_LIB=yes \ 903 | make_deps=yes 904 | pncl: profiling-native-code-library 905 | 906 | # packs byte-code objects 907 | pack-byte-code: $(PRE_TARGETS) 908 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(BCRESULT).cmo \ 909 | REAL_RESULT="$(BCRESULT)" \ 910 | PACK_LIB=yes make_deps=yes 911 | pabc: pack-byte-code 912 | 913 | # packs native-code objects 914 | pack-native-code: $(PRE_TARGETS) 915 | $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ 916 | $(NCRESULT).cmx $(NCRESULT).$(EXT_OBJ) \ 917 | REAL_RESULT="$(NCRESULT)" \ 918 | REAL_OCAMLC="$(OCAMLOPT)" \ 919 | PACK_LIB=yes make_deps=yes 920 | panc: pack-native-code 921 | 922 | # generates HTML-documentation 923 | htdoc: $(DOC_DIR)/$(RESULT)/html/index.html 924 | 925 | # generates Latex-documentation 926 | ladoc: $(DOC_DIR)/$(RESULT)/latex/doc.tex 927 | 928 | # generates PostScript-documentation 929 | psdoc: $(DOC_DIR)/$(RESULT)/latex/doc.ps 930 | 931 | # generates PDF-documentation 932 | pdfdoc: $(DOC_DIR)/$(RESULT)/latex/doc.pdf 933 | 934 | # generates all supported forms of documentation 935 | doc: htdoc ladoc psdoc pdfdoc 936 | 937 | ########################################################################### 938 | # LOW LEVEL RULES 939 | 940 | $(REAL_RESULT): $(REAL_IMPL_INTF) $(OBJ_LINK) $(EXTRADEPS) $(RESULTDEPS) 941 | $(REAL_OCAMLFIND) $(REAL_OCAMLC) \ 942 | $(OCAML_FIND_PACKAGES) $(OCAML_FIND_LINKPKG) \ 943 | $(ALL_LDFLAGS) $(OBJS_LIBS) -o $@$(EXE) \ 944 | $(REAL_IMPL) 945 | 946 | nolink: $(REAL_IMPL_INTF) $(OBJ_LINK) 947 | 948 | ifdef WIN32 949 | $(REAL_RESULT).dll: $(REAL_IMPL_INTF) $(OBJ_LINK) 950 | $(CAMLIDLDLL) $(CAMLIDLDLLFLAGS) $(OBJ_LINK) $(CLIBS) \ 951 | -o $@ $(REAL_IMPL) 952 | endif 953 | 954 | %$(TOPSUFFIX): $(REAL_IMPL_INTF) $(OBJ_LINK) $(EXTRADEPS) 955 | $(REAL_OCAMLFIND) $(OCAMLMKTOP) \ 956 | $(OCAML_FIND_PACKAGES) $(OCAML_FIND_LINKPKG) \ 957 | $(ALL_LDFLAGS) $(OBJS_LIBS) -o $@$(EXE) \ 958 | $(REAL_IMPL) 959 | 960 | .SUFFIXES: .mli .ml .cmi .cmo .cmx .cma .cmxa .$(EXT_OBJ) \ 961 | .mly .di .d .$(EXT_LIB) .idl %.oxridl .c .m .$(EXT_CXX) .h .so \ 962 | .rep .zog .glade 963 | 964 | ifndef STATIC 965 | ifdef MINGW 966 | # From OCaml 3.11.0, ocamlmklib is available on windows 967 | OCAMLMLIB_EXISTS = $(shell which $(OCAMLMKLIB)) 968 | ifeq ($(strip $(OCAMLMLIB_EXISTS)),) 969 | $(DLLSONAME): $(OBJ_LINK) 970 | $(CC) $(CFLAGS) $(CFLAGS_WIN32) $(OBJ_LINK) -shared -o $@ \ 971 | $(wildcard $(foreach dir,$(LIBDIRS),$(CLIBS:%=$(dir)/lib%.a))) \ 972 | '$(OCAMLLIBPATH)/ocamlrun.a' \ 973 | -Wl,--whole-archive \ 974 | -Wl,--export-all-symbols \ 975 | -Wl,--allow-multiple-definition \ 976 | -Wl,--enable-auto-import 977 | else 978 | $(DLLSONAME): $(OBJ_LINK) 979 | $(OCAMLMKLIB) $(INCFLAGS) $(CLIBFLAGS) \ 980 | -o $(CLIB_BASE) $(OBJ_LINK) $(CLIBS:%=-l%) \ 981 | $(CFRAMEWORKS:%=-framework %) \ 982 | $(OCAMLMKLIB_FLAGS) 983 | endif 984 | else 985 | ifdef MSVC 986 | $(DLLSONAME): $(OBJ_LINK) 987 | link /NOLOGO /DLL /OUT:$@ $(OBJ_LINK) \ 988 | $(wildcard $(foreach dir,$(LIBDIRS),$(CLIBS:%=$(dir)/%.lib))) \ 989 | '$(OCAMLLIBPATH)/ocamlrun.lib' 990 | 991 | else 992 | $(DLLSONAME): $(OBJ_LINK) 993 | $(OCAMLMKLIB) $(INCFLAGS) $(CLIBFLAGS) \ 994 | -o $(CLIB_BASE) $(OBJ_LINK) $(CLIBS:%=-l%) $(CFRAMEWORKS:%=-framework %) \ 995 | $(OCAMLMKLIB_FLAGS) 996 | endif 997 | endif 998 | endif 999 | 1000 | ifndef LIB_PACK_NAME 1001 | $(RESULT).cma: $(REAL_IMPL_INTF) $(MAKEDLL) $(EXTRADEPS) $(RESULTDEPS) 1002 | $(REAL_OCAMLFIND) $(REAL_OCAMLC) -a $(ALL_LDFLAGS) $(OBJS_LIBS) -o $@ $(REAL_IMPL) 1003 | 1004 | $(RESULT).cmxa $(RESULT).$(EXT_LIB): $(REAL_IMPL_INTF) $(EXTRADEPS) $(RESULTDEPS) 1005 | $(REAL_OCAMLFIND) $(OCAMLOPT) -a $(ALL_LDFLAGS) $(OBJS_LIBS) -o $@ $(REAL_IMPL) 1006 | else 1007 | # Packing a bytecode library 1008 | ifdef BYTE_OCAML 1009 | $(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmo: $(REAL_IMPL_INTF) 1010 | $(REAL_OCAMLFIND) $(REAL_OCAMLC) -pack -o $(LIB_PACK_NAME).cmo $(OCAMLLDFLAGS) $(REAL_IMPL) 1011 | # Packing into a unit which can be transformed into a library 1012 | # Remember the .ml's must have been compiled with -for-pack $(LIB_PACK_NAME) 1013 | else 1014 | $(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmx: $(REAL_IMPL_INTF) 1015 | $(REAL_OCAMLFIND) $(OCAMLOPT) -pack -o $(LIB_PACK_NAME).cmx $(OCAMLLDFLAGS) $(REAL_IMPL) 1016 | endif 1017 | 1018 | $(RESULT).cma: $(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmo $(MAKEDLL) $(EXTRADEPS) $(RESULTDEPS) 1019 | $(REAL_OCAMLFIND) $(REAL_OCAMLC) -a $(ALL_LDFLAGS) $(OBJS_LIBS) -o $@ $(LIB_PACK_NAME).cmo 1020 | 1021 | $(RESULT).cmxa $(RESULT).$(EXT_LIB): $(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmx $(EXTRADEPS) $(RESULTDEPS) 1022 | $(REAL_OCAMLFIND) $(OCAMLOPT) -a $(filter-out -custom, $(ALL_LDFLAGS)) $(OBJS_LIBS) -o $@ $(LIB_PACK_NAME).cmx 1023 | endif 1024 | 1025 | $(RES_CLIB): $(OBJ_LINK) 1026 | ifndef MSVC 1027 | ifneq ($(strip $(OBJ_LINK)),) 1028 | $(AR) rcs $@ $(OBJ_LINK) 1029 | endif 1030 | else 1031 | ifneq ($(strip $(OBJ_LINK)),) 1032 | lib -nologo -debugtype:cv -out:$(RES_CLIB) $(OBJ_LINK) 1033 | endif 1034 | endif 1035 | 1036 | .mli.cmi: $(EXTRADEPS) 1037 | $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ 1038 | if [ -z "$$pp" ]; then \ 1039 | $(ECHO) $(REAL_OCAMLFIND) $(INTF_OCAMLC) $(OCAML_FIND_PACKAGES) \ 1040 | -c $(THREAD_FLAG) $(ANNOT_FLAG) \ 1041 | $(OCAMLFLAGS) $(INCFLAGS) $<; \ 1042 | $(REAL_OCAMLFIND) $(INTF_OCAMLC) $(OCAML_FIND_PACKAGES) \ 1043 | -c $(THREAD_FLAG) $(ANNOT_FLAG) \ 1044 | $(OCAMLFLAGS) $(INCFLAGS) $<; \ 1045 | else \ 1046 | $(ECHO) $(REAL_OCAMLFIND) $(INTF_OCAMLC) $(OCAML_FIND_PACKAGES) \ 1047 | -c -pp \"$$pp $(PPFLAGS)\" $(THREAD_FLAG) $(ANNOT_FLAG) \ 1048 | $(OCAMLFLAGS) $(INCFLAGS) $<; \ 1049 | $(REAL_OCAMLFIND) $(INTF_OCAMLC) $(OCAML_FIND_PACKAGES) \ 1050 | -c -pp "$$pp $(PPFLAGS)" $(THREAD_FLAG) $(ANNOT_FLAG) \ 1051 | $(OCAMLFLAGS) $(INCFLAGS) $<; \ 1052 | fi 1053 | 1054 | .ml.cmi .ml.$(EXT_OBJ) .ml.cmx .ml.cmo: $(EXTRADEPS) 1055 | $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ 1056 | if [ -z "$$pp" ]; then \ 1057 | $(ECHO) $(REAL_OCAMLFIND) $(REAL_OCAMLC) $(OCAML_FIND_PACKAGES) \ 1058 | -c $(ALL_OCAMLCFLAGS) $<; \ 1059 | $(REAL_OCAMLFIND) $(REAL_OCAMLC) $(OCAML_FIND_PACKAGES) \ 1060 | -c $(ALL_OCAMLCFLAGS) $<; \ 1061 | else \ 1062 | $(ECHO) $(REAL_OCAMLFIND) $(REAL_OCAMLC) $(OCAML_FIND_PACKAGES) \ 1063 | -c -pp \"$$pp $(PPFLAGS)\" $(ALL_OCAMLCFLAGS) $<; \ 1064 | $(REAL_OCAMLFIND) $(REAL_OCAMLC) $(OCAML_FIND_PACKAGES) \ 1065 | -c -pp "$$pp $(PPFLAGS)" $(ALL_OCAMLCFLAGS) $<; \ 1066 | fi 1067 | 1068 | .PRECIOUS: %.ml 1069 | %.ml: %.mll 1070 | $(OCAMLLEX) $(LFLAGS) $< 1071 | 1072 | .PRECIOUS: %.ml %.mli 1073 | %.ml %.mli: %.mly 1074 | $(OCAMLYACC) $(YFLAGS) $< 1075 | $(QUIET)pp=`sed -n -e 's/.*(\*pp \([^*]*\) \*).*/\1/p;q' $<`; \ 1076 | if [ ! -z "$$pp" ]; then \ 1077 | mv $*.ml $*.ml.temporary; \ 1078 | echo "(*pp $$pp $(PPFLAGS)*)" > $*.ml; \ 1079 | cat $*.ml.temporary >> $*.ml; \ 1080 | rm $*.ml.temporary; \ 1081 | mv $*.mli $*.mli.temporary; \ 1082 | echo "(*pp $$pp $(PPFLAGS)*)" > $*.mli; \ 1083 | cat $*.mli.temporary >> $*.mli; \ 1084 | rm $*.mli.temporary; \ 1085 | fi 1086 | 1087 | 1088 | .PRECIOUS: %.ml 1089 | %.ml: %.rep 1090 | $(CAMELEON_REPORT) $(CAMELEON_REPORT_FLAGS) -gen $< 1091 | 1092 | .PRECIOUS: %.ml 1093 | %.ml: %.zog 1094 | $(CAMELEON_ZOGGY) $(CAMELEON_ZOGGY_FLAGS) -impl $< > $@ 1095 | 1096 | .PRECIOUS: %.ml 1097 | %.ml: %.glade 1098 | $(OCAML_GLADECC) $(OCAML_GLADECC_FLAGS) $< > $@ 1099 | 1100 | .PRECIOUS: %.ml %.mli 1101 | %.ml %.mli: %.oxridl 1102 | $(OXRIDL) $< 1103 | 1104 | .PRECIOUS: %.ml %.mli %_stubs.c %.h 1105 | %.ml %.mli %_stubs.c %.h: %.idl 1106 | $(CAMLIDL) $(MAYBE_IDL_HEADER) $(IDLFLAGS) \ 1107 | $(CAMLIDLFLAGS) $< 1108 | $(QUIET)if [ $(NOIDLHEADER) ]; then touch $*.h; fi 1109 | 1110 | .c.$(EXT_OBJ): 1111 | $(OCAMLC) -c -cc "$(CC)" -ccopt "$(CFLAGS) \ 1112 | $(CPPFLAGS) $(CPPFLAGS_WIN32) \ 1113 | $(CFLAGS_WIN32) $(CINCFLAGS) $(CFLAG_O)$@ " $< 1114 | 1115 | .m.$(EXT_OBJ): 1116 | $(CC) -c $(CFLAGS) $(CINCFLAGS) $(CPPFLAGS) \ 1117 | -I'$(OCAMLLIBPATH)' \ 1118 | $< $(CFLAG_O)$@ 1119 | 1120 | .$(EXT_CXX).$(EXT_OBJ): 1121 | $(CXX) -c $(CXXFLAGS) $(CINCFLAGS) $(CPPFLAGS) \ 1122 | -I'$(OCAMLLIBPATH)' \ 1123 | $< $(CFLAG_O)$@ 1124 | 1125 | $(MLDEPDIR)/%.d: %.ml 1126 | $(QUIET)if [ ! -d $(@D) ]; then mkdir -p $(@D); fi 1127 | $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ 1128 | if [ -z "$$pp" ]; then \ 1129 | $(ECHO) $(REAL_OCAMLFIND) $(OCAMLDEP) $(OCAML_DEP_PACKAGES) \ 1130 | $(DINCFLAGS) $< \> $@; \ 1131 | $(REAL_OCAMLFIND) $(OCAMLDEP) $(OCAML_DEP_PACKAGES) \ 1132 | $(DINCFLAGS) $< > $@; \ 1133 | else \ 1134 | $(ECHO) $(REAL_OCAMLFIND) $(OCAMLDEP) $(OCAML_DEP_PACKAGES) \ 1135 | -pp \"$$pp $(PPFLAGS)\" $(DINCFLAGS) $< \> $@; \ 1136 | $(REAL_OCAMLFIND) $(OCAMLDEP) $(OCAML_DEP_PACKAGES) \ 1137 | -pp "$$pp $(PPFLAGS)" $(DINCFLAGS) $< > $@; \ 1138 | fi 1139 | 1140 | $(BCDIDIR)/%.di $(NCDIDIR)/%.di: %.mli 1141 | $(QUIET)if [ ! -d $(@D) ]; then mkdir -p $(@D); fi 1142 | $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ 1143 | if [ -z "$$pp" ]; then \ 1144 | $(ECHO) $(REAL_OCAMLFIND) $(OCAMLDEP) $(DEPFLAGS) $(DINCFLAGS) $< \> $@; \ 1145 | $(REAL_OCAMLFIND) $(OCAMLDEP) $(DEPFLAGS) $(DINCFLAGS) $< > $@; \ 1146 | else \ 1147 | $(ECHO) $(REAL_OCAMLFIND) $(OCAMLDEP) $(DEPFLAGS) \ 1148 | -pp \"$$pp $(PPFLAGS)\" $(DINCFLAGS) $< \> $@; \ 1149 | $(REAL_OCAMLFIND) $(OCAMLDEP) $(DEPFLAGS) \ 1150 | -pp "$$pp $(PPFLAGS)" $(DINCFLAGS) $< > $@; \ 1151 | fi 1152 | 1153 | $(DOC_DIR)/$(RESULT)/html: 1154 | mkdir -p $@ 1155 | 1156 | $(DOC_DIR)/$(RESULT)/html/index.html: $(DOC_DIR)/$(RESULT)/html $(DOC_FILES) 1157 | rm -rf $