123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426(** *)openPrintfopenExtLibopenPreludemoduleType=structtypet=|Unitof[`Interval]|Int|Text|Blob|Float|Bool|Datetime|Decimal|Any[@@derivingshow{with_path=false}]letto_string=showletmatchesxy=matchx,ywith|Any,_|_,Any->true|_->x=yletis_unit=functionUnit_->true|_->falseletorderxy=ifx=ythen`Equalelsematchx,ywith|Any,t|t,Any->`Order(t,Any)|Int,Float|Float,Int->`Order(Int,Float)|Text,Blob|Blob,Text->`Order(Text,Blob)|Int,Datetime|Datetime,Int->`Order(Int,Datetime)|Text,Datetime|Datetime,Text->`Order(Datetime,Text)|_->`Noletcommon_typefxy=matchorderxywith|`Equal->Somex|`Orderp->Some(fp)|`No->Noneletcommon_supertype=common_typesndletcommon_subtype=common_typefstletcommon_typexy=Option.is_some@@common_subtypexytypetyvar=Typoft|Varofintletstring_of_tyvar=functionTypt->to_stringt|Vari->sprintf"'%c"(Char.chr@@Char.code'a'+i)typefunc=|Groupoft(* _ -> t *)|Agg(* 'a -> 'a *)|Multioftyvar*tyvar(* 'a -> ... -> 'a -> 'b *)|Retoft(* _ -> t *)(* TODO eliminate *)|Foftyvar*tyvarlistletmonomorphicretargs=F(Typret,List.map(funt->Typt)args)letfixed=monomorphicletidentity=F(Var0,[Var0])letpp_funcpp=letopenFormatinfunction|Agg->fprintfpp"|'a| -> 'a"|Groupret->fprintfpp"|_| -> %s"(to_stringret)|Retret->fprintfpp"_ -> %s"(to_stringret)|F(ret,args)->fprintfpp"%s -> %s"(String.concat" -> "@@List.mapstring_of_tyvarargs)(string_of_tyvarret)|Multi(ret,each_arg)->fprintfpp"{ %s }+ -> %s"(string_of_tyvareach_arg)(string_of_tyvarret)letstring_of_func=Format.asprintf"%a"pp_funcletis_grouping=function|Group_|Agg->true|Ret_|F_|Multi_->falseendmoduleConstraint=structtypeconflict_algo=|Ignore|Replace|Abort|Fail|Rollback[@@derivingshow{with_path=false},ord]typet=|PrimaryKey|NotNull|Null|Unique|Autoincrement|OnConflictofconflict_algo[@@derivingshow{with_path=false},ord]endmoduleConstraints=structincludeSet.Make(Constraint)letshows=[%derive.show:Constraint.tlist](elementss)letppfmts=Format.fprintffmt"%s"(shows)endtypeattr={name:string;domain:Type.t;extra:Constraints.t;}[@@derivingshow{with_path=false}]letmake_attributenamedomainextra={name;domain;extra}moduleSchema=structtypet=attrlist[@@derivingshow]exceptionErroroft*string(** FIXME attribute case sensitivity? *)letby_namename=functionattr->attr.name=nameletfind_by_nametname=List.find_all(by_namename)tletfindtname=matchfind_by_nametnamewith|[x]->x|[]->raise(Error(t,"missing attribute : "^name))|_->raise(Error(t,"duplicate attribute : "^name))letmake_unique=List.unique~cmp:(funa1a2->a1.name=a2.name&&a1.name<>"")letis_uniquet=List.length(make_uniquet)=List.lengthtletcheck_uniquet=is_uniquet||raise(Error(t,"duplicate attributes"))letprojectnamest=List.map(findt)namesletchange_inplacetbeforeafter=ignore(findtbefore);List.map(funattr->matchby_namebeforeattrwith|true->after|false->attr)tletexiststname=match(findtname:attr)with|_->true|exception_->falseletrenametoldnamenewname=ifnot(existstoldname)thenraise@@Error(t,"no such column : "^oldname);ifexiststnewnamethenraise@@Error(t,"column already exists : "^newname);List.map(funattr->ifattr.name=oldnamethen{attrwithname=newname}elseattr)tletcrosst1t2=t1@t2(** [contains t attr] tests whether schema [t] contains attribute [attr] *)letcontainstattr=findtattr.name=attrletcheck_containstattr=ifnot(containstattr)thenraise(Error(t,"type mismatch for attribute "^attr.name))letsubla=List.filter(funx->not(List.memxa))lletto_stringv=v|>List.map(funattr->sprintf"%s %s"(Type.to_stringattr.domain)attr.name)|>String.concat", "|>sprintf"[%s]"letnamest=t|>List.map(funattr->attr.name)|>String.concat","|>sprintf"[%s]"letnatural_t1t2=let(common,t1only)=List.partition(funx->List.memxt2)t1inif0=List.lengthcommonthenfailwith"natural'";lett2only=subt2commonincommon@t1only@t2onlyletnaturalt1t2=trynatural_t1t2with|_->raise(Error(t1,"no common attributes for natural join of "^(namest1)^" and "^(namest2)))letjoin_usinglt1t2=letcommon=List.map(findt1)linList.iter(check_containst2)common;common@subt1common@subt2commonletcheck_typest1t2=List.iter2(funa1a2->matcha1.domain,a2.domainwith|Type.Any,_|_,Type.Any->()|x,ywhenx=y->()|_->raise(Error(t1,sprintf"Atributes do not match : %s of type %s and %s of type %s"a1.name(Type.to_stringa1.domain)a2.name(Type.to_stringa2.domain))))t1t2letcheck_typest1t2=trycheck_typest1t2with|List.Different_list_size_->raise(Error(t1,(to_stringt1)^" differs in size to "^(to_stringt2)))letcompoundt1t2=check_typest1t2;t1letaddtcolpos=matchfind_by_nametcol.namewith|[]->beginmatchposwith|`First->col::t|`Default->t@[col]|`Aftername->trylet(i,_)=List.findi(fun_attr->by_namenameattr)tinlet(l1,l2)=List.split_nth(i+1)tinl1@(col::l2)withNot_found->raise(Error(t,"Can't insert column "^col.name^" after non-existing column "^name))end|_->raise(Error(t,"Already has column "^col.name))letdroptcol=ignore(findtcol);List.remove_if(by_namecol)tletchangetoldcolcolpos=matchposwith|`Default->change_inplacetoldcolcol|`First|`After_->add(droptoldcol)colposletto_string=showletprintx=prerr_endline(to_stringx)endtypetable_name={db:stringoption;tn:string}[@@derivingshow]letshow_table_name{db;tn}=matchdbwithSomedb->sprintf"%s.%s"dbtn|None->tnletmake_table_name?dbtn={db;tn}typeschema=Schema.t[@@derivingshow]typetable=table_name*schema[@@derivingshow]letprint_tableout(name,schema)=IO.write_lineout(show_table_namename);schema|>List.iterbeginfun{name;domain;extra}->IO.printfout"%10s %s %s\n"(Type.to_stringdomain)name(Constraints.showextra)end;IO.write_lineout""(** optional name and start/end position in string *)typeparam_id={label:stringoption;pos:int*int;}[@@derivingshow]typeparam={id:param_id;typ:Type.t;attr:attroption;}[@@derivingshow]letnew_param?attridtyp={id;typ;attr}typeparams=paramlist[@@derivingshow]typector=|Simpleofparam_id*varlistoption|Verbatimofstring*stringandvar=|Singleofparam|Choiceofparam_id*ctorlist[@@derivingshow]typevars=varlist[@@derivingshow]typealter_pos=[`Afterofstring|`Default|`First]typealter_action=[|`Addofattr*alter_pos|`RenameTableoftable_name|`RenameColumnofstring*string|`RenameIndexofstring*string|`Dropofstring|`Changeofstring*attr*alter_pos|`None]typeselect_result=(schema*paramlist)typedirection=[`Fixed|`Paramofparam_id][@@derivingshow]typeint_or_param=[`Constofint|`Limitofparam]typelimit_t=[`Limit|`Offset]typecol_name={cname:string;(** column name *)tname:table_nameoption;}andlimit=paramlist*boolandnested=source*(source*join_cond)listandsource=[`Selectofselect_full|`Tableoftable_name|`Nestedofnested]*table_nameoption(* alias *)andjoin_cond=[`Cross|`Searchofexpr|`Default|`Natural|`Usingofstringlist]andselect={columns:columnlist;from:nestedoption;where:exproption;group:exprlist;having:exproption;}andselect_full={select:select*selectlist;order:order;limit:limitoption;}andorder=(expr*directionoption)listand'exprchoices=(param_id*'exproption)listandexpr=|ValueofType.t(** literal value *)|Paramofparam|Choicesofparam_id*exprchoices|FunofType.func*exprlist(** parameters *)|Selectofselect_full*[`AsValue|`Exists]|Columnofcol_name|Insertedofstring(** inserted value *)andcolumn=|All|AllOfoftable_name|Exprofexpr*stringoption(** name *)[@@derivingshow{with_path=false}]typecolumns=columnlist[@@derivingshow]typeexpr_q=[`ValueofType.t(** literal value *)|`Paramofparam|`Choiceofparam_id*expr_qchoices|`FuncofType.func*expr_qlist(** return type, grouping, parameters *)][@@derivingshow]letexpr_to_string=show_exprtypeassignments=(col_name*expr)listtypeinsert_action={target:table_name;action:[`Setofassignmentsoption|`Valuesof(stringlistoption*[`Exprofexpr|`Default]listlistoption)(* column names * list of value tuples *)|`Selectof(stringlistoption*select_full)];on_duplicate:assignmentsoption;}typestmt=|Createoftable_name*[`Schemaofschema|`Selectofselect_full]|Dropoftable_name|Alteroftable_name*alter_actionlist|Renameof(table_name*table_name)list|CreateIndexofstring*table_name*stringlist(* index name, table name, columns *)|Insertofinsert_action|Deleteoftable_name*exproption|Setofstring*expr|Updateoftable_name*assignments*exproption*order*paramlist(* where, order, limit *)|UpdateMultiofsourcelist*assignments*exproption|Selectofselect_full|CreateRoutineofstring*Type.toption*(string*Type.t*exproption)list(*
open Schema
let test = [{name="a";domain=Type.Int}; {name="b";domain=Type.Int}; {name="c";domain=Type.Text};];;
let () = print test
let () = print (project ["b";"c";"b"] test)
let () = print (project ["b";"d"] test)
let () = print (rename test "a" "new_a")
*)moduleFunction:sigvallookup:string->int->Type.funcvaladd:int->Type.func->string->unitvalexclude:int->string->unitvalmonomorphic:Type.t->Type.tlist->string->unitvalmulti:ret:Type.tyvar->Type.tyvar->string->unitvalmulti_polymorphic:string->unitend=structleth=Hashtbl.create10letadd_nargtypname=letname=String.lowercasenameinifHashtbl.memh(name,narg)thenletfunc=matchnargwithNone->sprintf"%S"name|Somen->sprintf"%S of %d arguments"nameninfail"Function %s already registered"funcelseHashtbl.addh(name,narg)typletexcludenargname=add_(Somenarg)Nonenameletadd_multitypname=add_None(Sometyp)nameletaddnargtypname=add_(Somenarg)(Sometyp)nameletsponge=Type.(Multi(TypAny,TypAny))letlookupnamenarg=letname=String.lowercasenameinmatchHashtbl.findh(name,Somenarg)with|None->eprintfn"W: wrong number of arguments for known function %S, treating as untyped"name;sponge|Somet->t|exception_->matchHashtbl.findh(name,None)with|None->assertfalse|Somet->t|exception_->eprintfn"W: unknown function %S of %d arguments, treating as untyped"namenarg;spongeletmonomorphicretargsname=add(List.lengthargs)Type.(monomorphicretargs)nameletmulti_polymorphicname=add_multiType.(Multi(Var0,Var0))nameletmulti~retargsname=add_multiType.(Multi(ret,args))nameendlet()=letopenTypeinletopenFunctioninlet(||>)xf=List.iterfxin"count"|>add0(GroupInt);(* count( * ) - asterisk is treated as no parameters in parser *)"count"|>add1(GroupInt);"avg"|>add1(GroupFloat);["max";"min";"sum"]||>add1Agg;["max";"min"]||>multi_polymorphic;(* sqlite3 *)["lower";"upper"]||>monomorphicText[Text];"length"|>monomorphicInt[Text];["random"]||>monomorphicInt[];["nullif";"ifnull"]||>add2(F(Var0,[Var0;Var0]));["least";"greatest";"coalesce"]||>multi_polymorphic;"strftime"|>exclude1;(* requires at least 2 arguments *)["concat";"strftime"]||>multi~ret:(TypText)(TypText);["date";"time"]||>monomorphicText[Datetime];"julianday"|>multi~ret:(TypFloat)(TypText);"from_unixtime"|>monomorphicDatetime[Int];"from_unixtime"|>monomorphicText[Int;Text];["pow";"power"]||>monomorphicFloat[Float;Int];"unix_timestamp"|>monomorphicInt[];"unix_timestamp"|>monomorphicInt[Datetime];["timestampdiff";"timestampadd"]||>monomorphicInt[Unit`Interval;Datetime;Datetime];"any_value"|>add1(F(Var0,[Var0]));(* 'a -> 'a but not aggregate *)"substring"|>monomorphicText[Text;Int];"substring"|>monomorphicText[Text;Int;Int];"substring_index"|>monomorphicText[Text;Text;Int];"last_insert_id"|>monomorphicInt[];"last_insert_id"|>monomorphicInt[Int];()