From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Original-To: caml-list@sympa.inria.fr Delivered-To: caml-list@sympa.inria.fr Received: from mail3-relais-sop.national.inria.fr (mail3-relais-sop.national.inria.fr [192.134.164.104]) by sympa.inria.fr (Postfix) with ESMTPS id 7F7AC7EEEF for ; Tue, 23 Jun 2015 20:09:27 +0200 (CEST) Received-SPF: None (mail3-smtp-sop.national.inria.fr: no sender authenticity information available from domain of andre@digirati.com.br) identity=pra; client-ip=187.73.32.199; receiver=mail3-smtp-sop.national.inria.fr; envelope-from="andre@digirati.com.br"; x-sender="andre@digirati.com.br"; x-conformance=sidf_compatible Received-SPF: Pass (mail3-smtp-sop.national.inria.fr: domain of andre@digirati.com.br designates 187.73.32.199 as permitted sender) identity=mailfrom; client-ip=187.73.32.199; receiver=mail3-smtp-sop.national.inria.fr; envelope-from="andre@digirati.com.br"; x-sender="andre@digirati.com.br"; x-conformance=sidf_compatible; x-record-type="v=spf1" Received-SPF: Pass (mail3-smtp-sop.national.inria.fr: domain of postmaster@mta123.f1.k8.com.br designates 187.73.32.199 as permitted sender) identity=helo; client-ip=187.73.32.199; receiver=mail3-smtp-sop.national.inria.fr; envelope-from="andre@digirati.com.br"; x-sender="postmaster@mta123.f1.k8.com.br"; x-conformance=sidf_compatible; x-record-type="v=spf1" X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: A0AxAgDrn4lVnMcgSbtbg2Rfgx68MgaHTUwBAQEBAQESAQEBAQEICwkJIS6ETB0BATYCOxYLAgsDAgECAQ4LAgEvDAEIAQGILQu1RHCEZAEFkgwGkwQMLxKBMYwVh2+CJIFQZIZ+gXeGJhNzhB6KbQKBCYMxVYJIAQEB X-IPAS-Result: A0AxAgDrn4lVnMcgSbtbg2Rfgx68MgaHTUwBAQEBAQESAQEBAQEICwkJIS6ETB0BATYCOxYLAgsDAgECAQ4LAgEvDAEIAQGILQu1RHCEZAEFkgwGkwQMLxKBMYwVh2+CJIFQZIZ+gXeGJhNzhB6KbQKBCYMxVYJIAQEB X-IronPort-AV: E=Sophos;i="5.13,666,1427752800"; d="asc'?scan'208";a="137541351" Received: from mta123.f1.k8.com.br ([187.73.32.199]) by mail3-smtp-sop.national.inria.fr with ESMTP/TLS/ADH-AES256-SHA; 23 Jun 2015 20:09:25 +0200 Received: from localhost (localhost [127.0.0.1]) by smtpz.f1.k8.com.br (Postfix) with ESMTP id 0A2DD6047A for ; Tue, 23 Jun 2015 18:09:21 +0000 (UTC) X-Virus-Scanned: amavisd-new at k8.com.br Received: from smtpz.f1.k8.com.br ([127.0.0.1]) by localhost (mta123.f1.k8.com.br [127.0.0.1]) (amavisd-new, port 10024) with LMTP id JK-84tg-CS5t for ; Tue, 23 Jun 2015 18:09:19 +0000 (UTC) Received: from [10.200.12.21] (unknown [201.76.188.234]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtpz.f1.k8.com.br (Postfix) with ESMTPSA id 5879A604A1 for ; Tue, 23 Jun 2015 18:09:19 +0000 (UTC) X-DKIM: OpenDKIM Filter v2.6.8 smtpz.f1.k8.com.br 5879A604A1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=digirati.com.br; s=default; t=1435082959; bh=JKZctOqq8Pl6lnUICqAZV6A6ggco2IOhKpHfKb1YeiM=; h=Date:From:To:Subject; b=b4t7YZUS43NWYjaNbweWTYqKOm3q9cqcYLWKt3gHEJH1EJojs1DnldK+pP/WV7ron h2XNeGxx7ELuLdJ+Y8GSJzRT4F5n7NSnYn/IdI/ZKuZC0v5ShzymkQDfYWs8sbxwst kUEmikjVhQkOUEcFcmL+fEg1mwRZQMZnmu5c59nk= Message-ID: <5589A0CB.1000705@digirati.com.br> Date: Tue, 23 Jun 2015 15:09:15 -0300 From: Andre Nathan User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.7.0 MIME-Version: 1.0 To: caml users Content-Type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="8cL4sm9ktkMxRNjXruLb6l2tM6FX7MM1F" Subject: [Caml-list] [ANN?] Sequoia: MySQL query builder via syntax tree manipulation This is an OpenPGP/MIME signed message (RFC 4880 and 3156) --8cL4sm9ktkMxRNjXruLb6l2tM6FX7MM1F Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Hello So this was mostly an experiment for me to learn how to work with PPX extensions. It's a hack that I'm sure will affect people's sensibilities about what should be done with PPX, though maybe someone will find it useful. It's far from complete so I didn't bother creating an OPAM package yet. https://github.com/andrenth/sequoia The idea is to have a safe query builder for MySQL. You map a database using an OCaml module like so: module MyDB =3D struct type my_table =3D { id : int [@sql]; name : string [@sql]; } [@@sql] end [@@sql] The @sql/@@sql attributes can take an extra string that will map to the real MySQL column/table/database names in case they differ from their OCaml names. With that definition you can then build a query: let%sql query =3D select t.name from MyDB.my_table as_ t Which will build the query below via `Sequoia.Select.to_string query`: SELECT `t`.`name` FROM `MyDB`.`my_table` AS `t` Variable substitution is supported using the ref syntax: let pat =3D Sequoia.string "a%" let%sql query =3D select t.name from MyDB.my_table as_ t where (t.name like !pat && t.id > 1) If you reference a database, table or column that isn't declared via the @sql/@@sql attributes or call a nonexistent MySQL function, you get a compile-time error: let%sql query =3D select (frob(t.name)) from MyDB.my_table as_ t =3D> This expression has type [> `Frob of [> `Column of string list ] ] but an expression was expected of type Sequoia.Function.t The second variant type does not allow tag(s) `Frob If you try to select a column from a table that's not in a from or join clause, you get an error too: let%sql query =3D select MyDB.my_table.name =3D> Table `my_table` not selected in query There are many problems though. Off the top of my head: * Everything must be lowercase; * Error reporting sucks; * You need parentheses around OCaml expressions that wouldn't be needed in real SQL; * Lots of MySQL functions are not defined yet so you'll get bogus errors; * Due to syntax clash you need stuff like "as_" or "in_"; * Need to find a way to make it work when the DB definitions are in separate files; * Conversion to string doesn't indent the query and is really parenthesis-happy. * Everybody hates MySQL. Cheers! Andre --8cL4sm9ktkMxRNjXruLb6l2tM6FX7MM1F Content-Type: application/pgp-signature; name="signature.asc" Content-Description: OpenPGP digital signature Content-Disposition: attachment; filename="signature.asc" -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQEcBAEBAgAGBQJViaDPAAoJED4JW1qwFY2c2RgIAKHTiCnR+Js/r8a2jU+EKnCq bh8F/kdUyyybJ0BXbjxnM4HtVc/zXV8UI7YNckLK6yBPQqr4t27mN+AyW8msgBCD Vf8r73ox8Yq3wDqhrG6EaE2gxZx39MrfYOLy1gkY8/Y84ZvcNYm5eY2EO/X67CxY cNL4cntAuOzDvQc0ruF+kN4AyFOI4CMiS5IAHkYoBl8TSn/Amz4i/twUM0X+ugWq WPC352pTW1AEqb+/ku/pbf7lien2pQx+XXmzUxcwjTPwNJQGPlBF8MVjqphmgnpc jpdYmXKm9IcL+G2M2AiQ/Ji4pYPPiM+0JTww17P4w17aeASmBkaUCUJiWU3qOBs= =8vfk -----END PGP SIGNATURE----- --8cL4sm9ktkMxRNjXruLb6l2tM6FX7MM1F--