Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
schema.ml1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292type table_name = Types.table_name type conflict_clause = [`ROLLBACK | `ABORT | `FAIL | `IGNORE | `REPLACE] type foreign_conflict_clause = [`SET_NULL | `SET_DEFAULT | `CASCADE | `RESTRICT | `NO_ACTION ] type 'a sql_constraint = | PrimaryKey of { name: string option; ordering: [`ASC | `DESC] option; local_columns: string list option; on_conflict: conflict_clause option; auto_increment: bool } | NotNull of { name: string option; on_conflict: conflict_clause option } | Unique of { name: string option; local_columns: string list option; on_conflict: conflict_clause option; } | ForeignKey of { local_columns: string list option; name: string option; table: Types.table_name; columns: string list; on_update: foreign_conflict_clause option; on_delete: foreign_conflict_clause option; } type 'a field = string * 'a Type.t * [`Column] sql_constraint list type 'a constraint_ = 'a sql_constraint let pp_conflict_clause fmt = function | `ROLLBACK -> Format.fprintf fmt "ON CONFLICT ROLLBACK" | `ABORT -> Format.fprintf fmt "ON CONFLICT ABORT" | `FAIL -> Format.fprintf fmt "ON CONFLICT FAIL" | `IGNORE -> Format.fprintf fmt "ON CONFLICT IGNORE" | `REPLACE -> Format.fprintf fmt "ON CONFLICT REPLACE" let pp_foreign_conflict_clause fmt = function | `SET_NULL -> Format.fprintf fmt "SET NULL" | `SET_DEFAULT -> Format.fprintf fmt "SET DEFAULT" | `CASCADE -> Format.fprintf fmt "CASCADE" | `RESTRICT -> Format.fprintf fmt "RESTRICT" | `NO_ACTION -> Format.fprintf fmt "NO ACTION" let pp_opt f fmt = function None -> () | Some v -> Format.fprintf fmt " %a" f v let pp_parens f fmt = fun v -> Format.fprintf fmt "(%a)" f v let pp_ordering fmt = function | `ASC -> Format.fprintf fmt "ASC" | `DESC -> Format.fprintf fmt "DESC" let pp_column_list fmt ls = Format.pp_print_list ~pp_sep:(fun fmt () -> Format.fprintf fmt ", ") Format.pp_print_string fmt ls let pp_constraint_name fmt = function | None -> () | Some name -> Format.fprintf fmt "CONSTRAINT %s " name let pp_sql_constraint fmt = function | PrimaryKey { name; ordering; local_columns; on_conflict; auto_increment; } -> Format.fprintf fmt "%aPRIMARY KEY%a%a%a%a" pp_constraint_name name (pp_opt pp_ordering) ordering (pp_opt (pp_parens pp_column_list)) local_columns (pp_opt pp_conflict_clause) on_conflict (fun fmt vl -> if vl then Format.fprintf fmt " AUTOINCREMENT") auto_increment | NotNull { name; on_conflict } -> Format.fprintf fmt "%aNOT NULL%a" pp_constraint_name name (pp_opt pp_conflict_clause) on_conflict | Unique { name; local_columns; on_conflict } -> Format.fprintf fmt "%aUNIQUE%a%a" pp_constraint_name name (pp_opt (pp_parens pp_column_list)) local_columns (pp_opt pp_conflict_clause) on_conflict | ForeignKey { local_columns; name; table; columns; on_update; on_delete } -> Format.fprintf fmt "%a%a%sREFERENCES %s %a%a%a" pp_constraint_name name (pp_opt (fun fmt vl -> Format.fprintf fmt "FOREIGN KEY %a" (pp_parens pp_column_list) vl)) local_columns (if Option.is_some name || Option.is_some local_columns then " " else "") (snd table) (pp_parens pp_column_list) columns (pp_opt (fun fmt vl -> Format.fprintf fmt "ON UPDATE %a" pp_foreign_conflict_clause vl)) on_update (pp_opt (fun fmt vl -> Format.fprintf fmt "ON DELETE %a" pp_foreign_conflict_clause vl)) on_delete let ensure_table_constraint : 'a sql_constraint -> unit = function | NotNull _ -> invalid_arg "NOT NULL constraints are not table constraints" | PrimaryKey { name=_; ordering; local_columns; on_conflict=_; auto_increment } -> if auto_increment then invalid_arg "PRIMARY KEY constraints when given as a table \ constraint can not auto-increment"; if Option.is_some ordering then invalid_arg "PRIMARY KEY constraints when given as a table \ constraint can not specify ordering"; if Option.is_none local_columns then invalid_arg "PRIMARY KEY constraints when given as a table \ constraint must specify columns explicitly"; | Unique { name=_; local_columns; on_conflict=_ } -> if Option.is_none local_columns then invalid_arg "UNIQUE constraints when given as a table \ constraint must specify columns explicitly"; | ForeignKey { local_columns; name=_; table=_; columns=_; on_update=_; on_delete=_ } -> if Option.is_none local_columns then invalid_arg "UNIQUE constraints when given as a table \ constraint must specify the local columns \ explicitly" let ensure_column_constraint : 'a sql_constraint -> unit = function | NotNull _ -> () | PrimaryKey { name=_; ordering=_; local_columns; on_conflict=_; auto_increment=_ } -> if Option.is_some local_columns then invalid_arg "PRIMARY KEY column constraints can not list \ columns explicitly"; | Unique { name=_; local_columns; on_conflict=_ } -> if Option.is_some local_columns then invalid_arg "UNIQUE column constraints can not list \ columns explicitly"; | ForeignKey { local_columns; name=_; table=_; columns=_; on_update=_; on_delete=_ } -> if Option.is_some local_columns then invalid_arg "FOREIGN KEY column constraints can not list \ columns explicitly" let field ?(constraints : _ list =[]) name ~ty : 'a field = List.iter ensure_column_constraint constraints; (name, ty, constraints) let field_name = function (name, _, _) -> name let primary_key ?name ?ordering ?on_conflict ?(auto_increment=false) () : [`Column] sql_constraint = PrimaryKey { name; ordering; local_columns=None; on_conflict; auto_increment } let table_primary_key ?name ?on_conflict columns : [`Table] sql_constraint = PrimaryKey { name; ordering=None; local_columns=Some columns; on_conflict; auto_increment=false } let not_null ?name ?on_conflict () : [`Column] sql_constraint = NotNull { name; on_conflict } let unique ?name ?on_conflict () : [`Column] sql_constraint = Unique { name; local_columns=None; on_conflict; } let table_unique ?name ?on_conflict columns : [`Table] sql_constraint = Unique { name; local_columns=Some columns; on_conflict; } let rec expr_list_to_column_names : 'a . Types.table_name -> 'a Expr.expr_list -> string list = fun (type a) table_name (ls: a Types.expr_list) : string list -> match ls with | [] -> [] | Types.FIELD (table_name', name, _) :: t -> if not (table_name = table_name') then invalid_arg "foreign key constraint uses fields from a \ different table than the one specified"; name :: expr_list_to_column_names table_name t | _ :: _ -> invalid_arg "foreign key constraint must operate on fields \ directly not derived expressions" let foreign_key ?name ?on_update ?on_delete ~table ~columns () : [`Column] sql_constraint = ForeignKey { local_columns=None; name; table; columns=expr_list_to_column_names table columns; on_update; on_delete; } let table_foreign_key ?name ?on_update ?on_delete ~table ~columns local_columns : [`Table] sql_constraint = ForeignKey { local_columns=Some local_columns; name; table; columns=expr_list_to_column_names table columns; on_update; on_delete; } let ty : 'a field -> 'a Type.t = function (_, ty, _) -> ty type 'a table = | [] : unit table | (::) : ('a field * 'b table) -> ('a * 'b) table let to_sql ~name table (constraints: 'a sql_constraint list) = let rec loop : 'a . string option -> 'a table -> string option = fun acc (type a) (table: a table) -> let update_acc_with acc res = let acc = match acc with None -> "" | Some v -> v ^ ", " in let acc = acc ^ res in Some acc in match table with | [] -> acc | ((f, ty, constraints) :: rest) -> let constraints_text = match constraints with [] -> "" | _ :: _ -> Format.asprintf " %a" (Format.pp_print_list ~pp_sep:Format.pp_print_space pp_sql_constraint) constraints in let acc = update_acc_with acc @@ f ^ " " ^ Type.show ty ^ constraints_text in loop acc rest in let acc = (loop None table) in let acc = match acc,constraints with | None, [] -> "" | Some acc, [] -> acc | None, constraints -> Format.asprintf "%a" (Format.pp_print_list ~pp_sep:(fun fmt () -> Format.fprintf fmt ", ") pp_sql_constraint) constraints | Some acc, constraints -> Format.asprintf "%s, %a" acc (Format.pp_print_list ~pp_sep:(fun fmt () -> Format.fprintf fmt ", ") pp_sql_constraint) constraints in Format.sprintf "CREATE TABLE IF NOT EXISTS %s (%s)" name acc