123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114typet=PostgreSQL|MariaDB|SQLitemoduletypeDIALECT=sigvalname:stringvaldefault_port:intoptionvaladmin_database:stringoptionvaldatabase_exists_sql:stringvalcreate_database_sql:string->stringvaldrop_database_sql:string->stringvaltimestamp_to_string:string->stringvalsupports_database_lifecycle:boolendmodulePostgreSQL_dialect:DIALECT=structletname="PostgreSQL"letdefault_port=Some5432letadmin_database=Some"postgres"letsupports_database_lifecycle=trueletdatabase_exists_sql="SELECT EXISTS(SELECT 1 FROM pg_database WHERE datname = $1)"letcreate_database_sqldb_name=Printf.sprintf"CREATE DATABASE %s"db_nameletdrop_database_sqldb_name=Printf.sprintf"DROP DATABASE IF EXISTS %s"db_namelettimestamp_to_stringcol=Printf.sprintf"%s::text"colendmoduleMariaDB_dialect:DIALECT=structletname="MariaDB"letdefault_port=Some3306letadmin_database=Some"mysql"letsupports_database_lifecycle=trueletdatabase_exists_sql="SELECT EXISTS(SELECT 1 FROM information_schema.SCHEMATA WHERE SCHEMA_NAME \
= ?)"letcreate_database_sqldb_name=(* Use backticks for identifier quoting, IF NOT EXISTS for idempotency *)Printf.sprintf"CREATE DATABASE IF NOT EXISTS `%s`"db_nameletdrop_database_sqldb_name=Printf.sprintf"DROP DATABASE IF EXISTS `%s`"db_namelettimestamp_to_stringcol=Printf.sprintf"CAST(%s AS CHAR)"colendmoduleSQLite_dialect:DIALECT=structletname="SQLite"letdefault_port=Noneletadmin_database=Noneletsupports_database_lifecycle=false(* These are unused for SQLite but required by signature *)letdatabase_exists_sql=""letcreate_database_sql_=""letdrop_database_sql_=""lettimestamp_to_stringcol=colend(** Normalize database URL for Caqti compatibility
SQLite: caqti-driver-sqlite3 expects sqlite3:path (single colon), not
sqlite3://path We accept both formats for user convenience but normalize to
what Caqti expects. *)letnormalize_url(url:string):string=letprefix="sqlite3://"inifString.starts_with~prefixurlthenletn=String.lengthprefixin"sqlite3:"^String.suburln(String.lengthurl-n)elseurlletdetect_from_url(url:string):(t,string)result=ifString.starts_with~prefix:"postgresql://"urlthenOkPostgreSQLelseifString.starts_with~prefix:"postgres://"urlthenOkPostgreSQLelseifString.starts_with~prefix:"mariadb://"urlthenOkMariaDBelseifString.starts_with~prefix:"mysql://"urlthenOkMariaDBelseifString.starts_with~prefix:"sqlite3://"urlthenOkSQLiteelseifString.starts_with~prefix:"sqlite3:"urlthenOkSQLiteelseletscheme=matchString.index_opturl':'with(* Grab "scheme://" for the message, but clamp the length: a URL like
"ht:" has no room for the "//" and would otherwise overflow String.sub. *)|Someidx->String.suburl0(min(idx+3)(String.lengthurl))|None->urlinError(Printf.sprintf"Unsupported database URL scheme: '%s'\n\n\
Supported databases:\n\
- PostgreSQL: postgresql:// or postgres://\n\
- MariaDB/MySQL: mariadb:// or mysql://\n\
- SQLite: sqlite3:// or sqlite3:\n\n\
Examples:\n\
- postgresql://user@localhost:5432/mydb\n\
- mariadb://root@localhost:3306/mydb\n\
- sqlite3://./dev.db (normalized to sqlite3:./dev.db)\n\
- sqlite3::memory: (in-memory database)\n\n"scheme)letget_dialect(db_type:t):(moduleDIALECT)=matchdb_typewith|PostgreSQL->(modulePostgreSQL_dialect)|MariaDB->(moduleMariaDB_dialect)|SQLite->(moduleSQLite_dialect)letto_string=function|PostgreSQL->"PostgreSQL"|MariaDB->"MariaDB"|SQLite->"SQLite"