diff --git a/README.md b/README.md index e2ad2e0c6..818ba0e8f 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Libraries: * tyxml (need version 3) * ipaddr (need version >= 2.1) * ocamlsqlite3 (tested with 2.0.2) OR + * pgocaml (tested with 2.3) OR * dbm (tested with 1.0) diff --git a/configure b/configure index 40ddb3870..45300869d 100755 --- a/configure +++ b/configure @@ -78,6 +78,7 @@ set_defaults () { enable_debug=1 enable_annot=1 with_sqlite=1 + with_pgsql=1 with_camlzip=1 with_dbm=1 with_preempt=1 @@ -107,7 +108,7 @@ full_pwd=`pwd` ## Which options exist? eoptions for enable/disable, woptions for with/without: eoptions="debug annot natdynlink" -woptions="sqlite dbm camlzip preempt" +woptions="pgsql sqlite dbm camlzip preempt" print_options () { for opt in $eoptions; do @@ -184,6 +185,7 @@ usage: ./configure [ options ] --enable-natdynlink, --disable-natdynlink Enable/disable nativecode dynamic linking --with-sqlite, --without-sqlite Compile ocsipersist with SQLite for persistent storage + --with-pgsql, --without-pgsql Compile ocsipersist with PostgreSQL for persistent storage --with-dbm, --without-dbm Compile ocsipersist with DBM for persistent storage --with-camlzip, --without-camlzip Compile deflatemod extension, requires camlzip @@ -424,6 +426,12 @@ check_library cryptokit "See: http://pauillac.inria.fr/~xleroy/software.html#cry check_library tyxml "See: http://ocsigen.org/tyxml/" +# Check PostgreSQL +case "$with_pgsql" in + 1) if test_library pgocaml; then with_pgsql=1; else with_pgsql=0; fi;; + 2) check_library pgocaml "https://github.com/darioteixeira/pgocaml";; +esac + # Check Sqlite3 case "$with_sqlite" in 1) if test_library sqlite3; then with_sqlite=1; else with_sqlite=0; fi;; @@ -514,6 +522,11 @@ if [ $with_sqlite -gt 0 ] ; then else with_sqlite="NO" fi +if [ $with_pgsql -gt 0 ] ; then + with_pgsql="YES" +else + with_pgsql="NO" +fi if [ $with_camlzip -gt 0 ] ; then with_camlzip="YES" else @@ -569,6 +582,9 @@ CAMLZIPNAME:=$zipname # Do you want ocsipersist with sqlite? YES/NO OCSIPERSISTSQLITE:=$with_sqlite +# Do you want ocsipersist with pgsql? YES/NO +OCSIPERSISTPGSQL:=$with_pgsql + # Do you want ocsipersist with dbm? YES/NO OCSIPERSISTDBM:=$with_dbm diff --git a/doc/manual-wiki/config.wiki b/doc/manual-wiki/config.wiki index f3be2f8cb..d8d60664e 100644 --- a/doc/manual-wiki/config.wiki +++ b/doc/manual-wiki/config.wiki @@ -148,7 +148,10 @@ Here is a simple example: }}} -Here is another example, for use as unprivileged user toto on port 8000: +Here is another example, for use as unprivileged user toto on port 8000. +We use the PostgreSQL (version >= 9.5) backend for ocsipersist. Note that the +ocsipersist-database under the specified name (default: "ocsipersist") needs to +exist. To create it run {{{psql}}} and execute {{{CREATE TABLE ocsipersist;}}}. {{{ @@ -167,8 +170,14 @@ Here is another example, for use as unprivileged user toto on port 8000: - - + + + port="3000" + user="toto" + password="toto's secret" + /> + + @@ -182,7 +191,7 @@ Here is another example, for use as unprivileged user toto on port 8000: - + diff --git a/doc/manual-wiki/libraries.wiki b/doc/manual-wiki/libraries.wiki index 481c75e23..c55b842c5 100644 --- a/doc/manual-wiki/libraries.wiki +++ b/doc/manual-wiki/libraries.wiki @@ -5,15 +5,17 @@ ==Ocsipersist -<
> is needed in {{{ eliom.cma }}}, thus you need to dynlink it in the configuration file before {{{ Eliom }}}). There are currently two implementations of {{{ Ocsipersist }}}: -{{{ ocsipersist-dbm.cma }}} (uses the DBM database) and -{{{ ocsipersist-sqlite.cma }}} (uses the SQLite database, -and depends on {{{ sqlite3.cma }}}). +{{{ ocsipersist-dbm.cma }}} (uses the DBM database); +{{{ ocsipersist-sqlite.cma }}} (uses the SQLite database +and depends on {{{ sqlite3.cma }}}); +{{{ ocsipersist-pgsql.cma }}} (uses the PostgreSQL (version >= 9.5) +database and depends on {{{ pa_pgsql.cma }}}); >> It is possible to customize the location of the database on the @@ -31,3 +33,20 @@ name of the {{{ocsidbm}}} process you want to use: }}} +PostgreSQL has more options to specify how to connect to the database. +Note that the ocsipersist-database under the specified name (default: +"ocsipersist") needs to exist. To create it run {{{psql}}} and execute +{{{CREATE TABLE ocsipersist;}}}. +{{{ + + + host="localhost" + port="3000" + user="Aerobic Respirator" + password="Guess what I need!" + database="ocsipersist" + unix_domain_socket_dir="./udsd" + size_conn_pool="16" + /> + +}}} diff --git a/opam b/opam index bff451014..68f9092c5 100644 --- a/opam +++ b/opam @@ -9,7 +9,7 @@ dev-repo: "https://github.com/ocsigen/ocsigenserver.git" license: "LGPL-2.1 with OCaml linking exception" build: [ ["sh" "configure" "--prefix" "%{prefix}%" "--ocsigen-user" "%{user}%" "--ocsigen-group" "%{group}%" "--commandpipe" "%{lib}%/ocsigenserver/var/run/ocsigenserver_command" "--logdir" "%{lib}%/ocsigenserver/var/log/ocsigenserver" "--mandir" "%{man}%/man1" "--docdir" "%{lib}%/ocsigenserver/share/doc/ocsigenserver" "--commandpipe" "%{lib}%/ocsigenserver/var/run/ocsigenserver_command" "--staticpagesdir" "%{lib}%/ocsigenserver/var/www" "--datadir" "%{lib}%/ocsigenserver/var/lib/ocsigenserver" "--sysconfdir" "%{lib}%/ocsigenserver/etc/ocsigenserver"] - [make] + [make "-j%{jobs}%" ] ] install: [make "install"] remove: [ @@ -30,6 +30,7 @@ depends: [ "tyxml" {> "3.6.0"} "dbm" "sqlite3" + "pgocaml" {>= "2.2"} "ipaddr" {>= "2.1"} "camlp4" ] diff --git a/src/Makefile.filelist b/src/Makefile.filelist index 6bcd747ff..bbbea29d1 100644 --- a/src/Makefile.filelist +++ b/src/Makefile.filelist @@ -82,6 +82,10 @@ ifeq "$(OCSIPERSISTSQLITE)" "YES" PLUGINS_IMPL += extensions/ocsipersist-sqlite.cma endif +ifeq "$(OCSIPERSISTPGSQL)" "YES" +PLUGINS_IMPL += extensions/ocsipersist-pgsql.cma +endif + ifeq "$(OCSIPERSISTDBM)" "YES" PLUGINS_IMPL += extensions/ocsipersist-dbm.cma PLUGINS_BIN += extensions/ocsipersist-dbm/ocsidbm diff --git a/src/extensions/Makefile b/src/extensions/Makefile index b7185f0f7..bce0f50b5 100644 --- a/src/extensions/Makefile +++ b/src/extensions/Makefile @@ -46,6 +46,14 @@ ifeq "$(NATDYNLINK)" "YES" opt:: ${FILES:.ml=.cmxs} endif +### PostgreSQL ### + +ifeq "$(OCSIPERSISTPGSQL)" "YES" +byte:: + $(MAKE) -C ocsipersist-pgsql byte +opt:: + $(MAKE) -C ocsipersist-pgsql opt +endif ### SQLite ### ifeq "$(OCSIPERSISTSQLITE)" "YES" @@ -80,6 +88,7 @@ endif clean: clean.local ${MAKE} -C ocsipersist-dbm clean ${MAKE} -C ocsipersist-sqlite clean + ${MAKE} -C ocsipersist-pgsql clean clean.local: -rm -f *.cm* *.o *.a *.annot -rm -f ${PREDEP} @@ -87,6 +96,8 @@ distclean: clean.local -rm -f *~ \#* .\#* ${MAKE} -C ocsipersist-dbm distclean ${MAKE} -C ocsipersist-sqlite distclean + ${MAKE} -C ocsipersist-pgsql distclean + -rm -f .depend ## Dependencies @@ -94,6 +105,7 @@ depend: ${PREDEP} $(OCAMLDEP) ${LIBS} *.mli *.ml > .depend ${MAKE} -C ocsipersist-dbm depend ${MAKE} -C ocsipersist-sqlite depend + ${MAKE} -C ocsipersist-pgsql depend FORCE: -include .depend diff --git a/src/extensions/ocsipersist-dbm/Makefile b/src/extensions/ocsipersist-dbm/Makefile index 58060ddf2..e2a9a09f2 100644 --- a/src/extensions/ocsipersist-dbm/Makefile +++ b/src/extensions/ocsipersist-dbm/Makefile @@ -71,6 +71,7 @@ clean: -rm -f ocsidbm ocsidbm.opt distclean: clean -rm -f *~ \#* .\#* + -rm -f .depend ## Dependencies diff --git a/src/extensions/ocsipersist-dbm/ocsipersist.ml b/src/extensions/ocsipersist-dbm/ocsipersist.ml index b09cc6459..b6e52ffae 100644 --- a/src/extensions/ocsipersist-dbm/ocsipersist.ml +++ b/src/extensions/ocsipersist-dbm/ocsipersist.ml @@ -237,7 +237,7 @@ let db_length store = (** Type of persistent data *) type 'a t = store * string -let open_store name : store = name +let open_store name = Lwt.return name let make_persistent_lazy_lwt ~store ~name ~default = let pvname = (store, name) in @@ -270,7 +270,7 @@ let set pvname v = (** Type of persistent tables *) type 'value table = string -let open_table name = name +let open_table name = Lwt.return name let table_name n = Lwt.return n diff --git a/src/extensions/ocsipersist-pgsql/Makefile b/src/extensions/ocsipersist-pgsql/Makefile new file mode 100644 index 000000000..3fe00d9a3 --- /dev/null +++ b/src/extensions/ocsipersist-pgsql/Makefile @@ -0,0 +1,69 @@ +include ../../../Makefile.config + +PACKAGE := tyxml.parser pgocaml.syntax lwt.syntax + +LIBS := -I ../../baselib -I ../../http -I ../../server \ + ${addprefix -package ,${PACKAGE}} +OCAMLC := $(OCAMLFIND) ocamlc${BYTEDBG} ${THREAD} +OCAMLOPT := $(OCAMLFIND) ocamlopt ${OPTDBG} ${THREAD} +OCAMLDOC := $(OCAMLFIND) ocamldoc +OCAMLDEP := $(OCAMLFIND) ocamldep + +all: byte opt + +### + +byte: ocsipersist-pgsql.cma +opt:: ocsipersist-pgsql.cmxa +ifeq "$(NATDYNLINK)" "YES" +opt:: ocsipersist-pgsql.cmxs +endif + +PREDEP := ocsipersist.mli + +ocsipersist-pgsql.cma: ocsipersist.cmo + $(OCAMLC) -a -o $@ $^ + cp ocsipersist.cmi .. + cp $@ .. + +ocsipersist-pgsql.cmxa: ocsipersist.cmx + $(OCAMLOPT) -a -o $@ $^ + cp ocsipersist.cmi .. + cp $@ ${patsubst %.cmxa,%.a,$@} .. + +ocsipersist-pgsql.cmxs: ocsipersist-pgsql.cmxa + $(OCAMLOPT) -shared -linkall -o $@ $^ + cp $@ .. + +ocsipersist.mli: + ln -s ../ocsipersist.mli . + +########## + +SYNTAX_FLAGS=-syntax camlp4o + +%.cmi: %.mli + $(OCAMLC) ${LIBS} -c $< +%.cmo: %.ml + $(OCAMLC) ${LIBS} ${SYNTAX_FLAGS} -c $< +%.cmx: %.ml + $(OCAMLOPT) ${LIBS} ${SYNTAX_FLAGS} -c $< +%.cmxs: %.cmxa + $(OCAMLOPT) -shared -linkall -o $@ $< + +## Clean up + +clean: + -rm -f *.cm[ioax] *.cmxa *.cmxs *.o *.a *.annot + -rm -f ${PREDEP} +distclean: clean + -rm -f *~ \#* .\#* + -rm -f .depend + +## Dependencies + +depend: ${PREDEP} + $(OCAMLDEP) ${LIBS} *.mli *.ml > .depend + +FORCE: +-include .depend diff --git a/src/extensions/ocsipersist-pgsql/ocsipersist.ml b/src/extensions/ocsipersist-pgsql/ocsipersist.ml new file mode 100644 index 000000000..23156beee --- /dev/null +++ b/src/extensions/ocsipersist-pgsql/ocsipersist.ml @@ -0,0 +1,211 @@ +(** PostgreSQL (>= 9.5) backend for Ocsipersist. *) + +let section = Lwt_log.Section.make "ocsipersist:sql" + +module Lwt_thread = struct + include Lwt + include Lwt_chan +end +module PGOCaml = PGOCaml_generic.Make(Lwt_thread) +open Lwt +open Printf + +exception Ocsipersist_error + +let host = ref None +let port = ref None +let user = ref None +let password = ref None +let database = ref "ocsipersist" +let unix_domain_socket_dir = ref None +let size_conn_pool = ref 16 + +let make_hashtbl () = Hashtbl.create 8 + +let connect () = + lwt dbhandle = PGOCaml.connect + ?host:!host + ?port:!port + ?user:!user + ?password:!password + ?database:(Some !database) + ?unix_domain_socket_dir:!unix_domain_socket_dir + () in + PGOCaml.set_private_data dbhandle @@ make_hashtbl (); + Lwt.return dbhandle + +let (>>) f g = f >>= fun _ -> g + +let conn_pool : (string, unit) Hashtbl.t PGOCaml.t Lwt_pool.t ref = + (* This connection pool will be overwritten by init_fun! *) + ref @@ Lwt_pool.create !size_conn_pool ~validate:PGOCaml.alive connect + +let use_pool f = Lwt_pool.use !conn_pool @@ fun db -> f db + +let key_value_of_row = function + | [Some key; Some value] -> (PGOCaml.bytea_of_string key, PGOCaml.bytea_of_string value) + | _ -> raise Ocsipersist_error + +(* get one value from the result of a query *) +let one = function + | [Some value]::xs -> PGOCaml.bytea_of_string value + | _ -> raise Not_found + +let marshal value = Marshal.to_string value [] +let unmarshal str = Marshal.from_string str 0 + +let prepare db query = + let hashtbl = PGOCaml.private_data db in + (* Get a unique name for this query using an MD5 digest. *) + let name = Digest.to_hex (Digest.string query) in + (* Have we prepared this statement already? If not, do so. *) + let is_prepared = Hashtbl.mem hashtbl name in + lwt () = if is_prepared then Lwt.return () else begin + PGOCaml.prepare db ~name ~query () >> + Lwt.return @@ Hashtbl.add hashtbl name () + end in + Lwt.return name + +let exec db query params = + lwt name = prepare db query in + let params = params |> List.map @@ fun x -> Some (PGOCaml.string_of_bytea x) in + PGOCaml.execute db ~name ~params () + +let cursor db query params f = + lwt name = prepare db query in + let params = params |> List.map @@ fun x -> Some (PGOCaml.string_of_bytea x) in + PGOCaml.cursor db ~name ~params @@ + fun row -> let (key,value) = key_value_of_row row in f + (PGOCaml.bytea_of_string key) + (unmarshal @@ PGOCaml.bytea_of_string value) + +let (@.) f g = fun x -> f (g x) (* function composition *) + +let create_table db table = + let query = sprintf "CREATE TABLE IF NOT EXISTS %s \ + (key TEXT, value BYTEA, PRIMARY KEY(key))" table + in exec db query [] >> Lwt.return () + + +type store = string + +type 'a t = { + store : string; + name : string; +} + +let open_store store = use_pool @@ fun db -> + create_table db store >> Lwt.return store + +let make_persistent_worker ~store ~name ~default db = + let query = sprintf "INSERT INTO %s VALUES ( $1 , $2 ) + ON CONFLICT ( key ) DO NOTHING" store in + (* NOTE: incompatible with < 9.5 *) + exec db query [name; marshal default] >> Lwt.return {store; name} + +let make_persistent ~store ~name ~default = + use_pool @@ fun db -> make_persistent_worker ~store ~name ~default db + +let make_persistent_lazy_lwt ~store ~name ~default = use_pool @@ fun db -> + let query = sprintf "SELECT 1 FROM %s WHERE key = $1 " store in + lwt result = exec db query [name] in + match result with + | [] -> + lwt default = default () in + make_persistent_worker ~store ~name ~default db + | _ -> Lwt.return {store = store; name = name} + +let make_persistent_lazy ~store ~name ~default = + let default () = Lwt.wrap default in + make_persistent_lazy_lwt ~store ~name ~default + +let get p = use_pool @@ fun db -> + let query = sprintf "SELECT value FROM %s WHERE key = $1 " p.store in + Lwt.map (unmarshal @. one) (exec db query [p.name]) + +let set p v = use_pool @@ fun db -> + let query = sprintf "UPDATE %s SET value = $2 WHERE key = $1 " p.store + in exec db query [p.name; marshal v] >> Lwt.return () + +type 'value table = string + +let table_name table = Lwt.return table + +let open_table table = use_pool @@ fun db -> + create_table db table >> Lwt.return table + +let find table key = use_pool @@ fun db -> + let query = sprintf "SELECT value FROM %s WHERE key = $1 " table in + Lwt.map (unmarshal @. one) (exec db query [key]) + +let add table key value = use_pool @@ fun db -> + let query = sprintf "INSERT INTO %s VALUES ( $1 , $2 ) + ON CONFLICT ( key ) DO UPDATE SET value = $2 " table + (* NOTE: incompatible with < 9.5 *) + in exec db query [key; marshal value] >> Lwt.return () + +let replace_if_exists table key value = use_pool @@ fun db -> + let query = sprintf "UPDATE %s SET value = $2 WHERE key = $1 RETURNING 0" table in + lwt result = exec db query [key; marshal value] in + match result with + | [] -> raise Not_found + | _ -> Lwt.return () + +let remove table key = use_pool @@ fun db -> + let query = sprintf "DELETE FROM %s WHERE key = $1 " table in + exec db query [key] >> Lwt.return () + +let length table = use_pool @@ fun db -> + let query = sprintf "SELECT count(*) FROM %s " table in + Lwt.map (unmarshal @. one) (exec db query []) + +let iter_step f table = use_pool @@ fun db -> + let query = sprintf "SELECT * FROM %s " table in + cursor db query [] f + +let iter_table = iter_step + +let fold_step f table x = + let res = ref x in + let g key value = + lwt res' = f key value !res in + res := res'; + Lwt.return () + in iter_step g table >> Lwt.return !res + +let fold_table = fold_step + +let iter_block a b = failwith "Ocsipersist.iter_block: not implemented" + + +open Simplexmlparser +let parse_global_config = function + | [] -> () + | [Element ("database", attrs, [])] -> let parse_attr = function + | ("host", h) -> host := Some h + | ("port", p) -> begin + try port := Some (int_of_string p) + with Failure _ -> raise @@ Ocsigen_extensions.Error_in_config_file + "port is not an integer" + end + | ("user", u) -> user := Some u + | ("password", pw) -> password := Some pw + | ("database", db) -> database := db + | ("unix_domain_socket_dir", udsd) -> unix_domain_socket_dir := Some udsd + | ("size_conn_pool", scp) -> begin + try size_conn_pool := int_of_string scp + with Failure _ -> raise @@ Ocsigen_extensions.Error_in_config_file + "size_conn_pool is not an integer" + end + | _ -> raise @@ Ocsigen_extensions.Error_in_config_file + "Unexpected attribute for in Ocsipersist config" + in ignore @@ List.map parse_attr attrs; () + | _ -> raise @@ Ocsigen_extensions.Error_in_config_file + "Unexpected content inside Ocsipersist config" + + +let init_fun config = + parse_global_config config; + conn_pool := Lwt_pool.create !size_conn_pool ~validate:PGOCaml.alive connect + +let _ = Ocsigen_extensions.register_extension ~name:"ocsipersist" ~init_fun () diff --git a/src/extensions/ocsipersist-sqlite/Makefile b/src/extensions/ocsipersist-sqlite/Makefile index 1721e7559..ab83ade60 100644 --- a/src/extensions/ocsipersist-sqlite/Makefile +++ b/src/extensions/ocsipersist-sqlite/Makefile @@ -58,6 +58,7 @@ clean: -rm -f ${PREDEP} distclean: clean -rm -f *~ \#* .\#* + -rm -f .depend ## Dependencies diff --git a/src/extensions/ocsipersist-sqlite/ocsipersist.ml b/src/extensions/ocsipersist-sqlite/ocsipersist.ml index 1238959ae..371e7a832 100644 --- a/src/extensions/ocsipersist-sqlite/ocsipersist.ml +++ b/src/extensions/ocsipersist-sqlite/ocsipersist.ml @@ -29,7 +29,7 @@ open Printf (** Data are divided into stores. Create one store for your project, where you will save all your data. *) -type store = string Lwt.t +type store = string exception Ocsipersist_error @@ -225,12 +225,11 @@ let db_length table = (** Type of persistent data *) type 'a t = string * string -let open_store name : store = +let open_store name = let s = "store___"^name in db_create s let make_persistent_lazy_lwt ~store ~name ~default = - store >>= fun store -> let pvname = (store, name) in (catch (fun () -> db_get pvname >>= (fun _ -> return ())) @@ -257,34 +256,29 @@ let set pvname v = db_replace pvname data (** Type of persistent tables *) -type 'value table = string Lwt.t +type 'value table = string (** name SHOULD NOT begin with "store___" *) let open_table name = db_create name -let table_name table = table +let table_name table = Lwt.return table let find table key = - table >>= fun table -> db_get (table, key) >>= fun v -> return (Marshal.from_string v 0) let add table key value = - table >>= fun table -> let data = Marshal.to_string value [] in db_replace (table, key) data let replace_if_exists table key value = - table >>= fun table -> let data = Marshal.to_string value [] in db_replace_if_exists (table, key) data let remove table key = - table >>= fun table -> db_remove (table, key) let iter_step f table = - table >>= fun table -> let rec aux rowid = db_iter_step table rowid >>= (function @@ -295,7 +289,6 @@ let iter_step f table = aux Int64.zero let fold_step f table beg = - table >>= fun table -> let rec aux rowid beg = db_iter_step table rowid >>= (function @@ -306,15 +299,14 @@ let fold_step f table beg = aux Int64.zero beg let iter_block f table = - table >>= fun table -> db_iter_block table f + let iter_table = iter_step let fold_table = fold_step let length table = - table >>= fun table -> db_length table diff --git a/src/extensions/ocsipersist.mli b/src/extensions/ocsipersist.mli index 575d4abec..8d31122d4 100644 --- a/src/extensions/ocsipersist.mli +++ b/src/extensions/ocsipersist.mli @@ -22,9 +22,9 @@ (** Persistent data on hard disk. *) (** - There are currently two implementations of this module, - one using a DBM database, and the other using SQLITE. - Link the one your want with your program. + There are currently three implementations of this module, + one using a DBM database, on the PostgreSQL database, + and one using SQLITE. Link the one your want with your program. *) @@ -41,7 +41,7 @@ type 'a t type store (** Open a store (and create it if it does not exist) *) -val open_store : string -> store +val open_store : string -> store Lwt.t val make_persistent : store:store -> name:string -> default:'a -> 'a t Lwt.t @@ -77,7 +77,7 @@ type 'value table val table_name : 'value table -> string Lwt.t (** Open a table (and create it if it does not exist) *) -val open_table : string -> 'value table +val open_table : string -> 'value table Lwt.t val find : 'value table -> string -> 'value Lwt.t (** [find table key] gives the value associated to [key]. diff --git a/src/files/META.in b/src/files/META.in index 1e3f38f15..357047e0d 100644 --- a/src/files/META.in +++ b/src/files/META.in @@ -151,6 +151,15 @@ package "ext" ( archive(native) = "ocsipersist-sqlite.cmxa" ) + package "ocsipersist-pgsql" ( + exists_if = "ocsipersist-pgsql.cma,ocsipersist-pgsql.cmxa" + requires = "pgocaml" + version = "[distributed with Ocsigen server]" + description = "Persistent data storage with PostgreSQL" + archive(byte) = "ocsipersist-pgsql.cma" + archive(native) = "ocsipersist-pgsql.cmxa" + ) + package "ocsipersist-dbm" ( exists_if = "ocsipersist-dbm.cma,ocsipersist-dbm.cmxa" version = "[distributed with Ocsigen server]" diff --git a/src/files/ocsigenserver.conf.in b/src/files/ocsigenserver.conf.in index 065af3345..c20da55ac 100644 --- a/src/files/ocsigenserver.conf.in +++ b/src/files/ocsigenserver.conf.in @@ -30,6 +30,21 @@ --> + + + + --> +