You might be interested in `dsn-connect` and the `data-source` structure (http://docs.racket-lang.org/db/connect.html#%28part._.Data_.Source_.Names%29).

Ryan


On 4/25/17 8:18 PM, David Storrs wrote:
Great.  Thanks, Phillip!

On Tue, Apr 25, 2017 at 2:14 PM, Philip McGrath
<phi...@philipmcgrath.com <mailto:phi...@philipmcgrath.com>> wrote:

     1. Yes, that was supposed to be (current-database-handle)
     2. A struct would definitely be a reasonable choice for the spec,
        but you might just want to add a failure result to the hash-ref
        calls inside initialize: in that case default-database-spec
        could just be #hasheq(), and you wouldn't have to explicitly
        specify a port, for example, to specify a different password.
        You're right though that the example should really have used
        checks for hash-has-key? in database-spec/c, as well, since it
        assumed those keys were present. In practice I would probably
        also require that the hash be immutable, though mostly for ease
        of reasoning.


    -Philip

    On Tue, Apr 25, 2017 at 12:48 PM, David Storrs
    <david.sto...@gmail.com <mailto:david.sto...@gmail.com>> wrote:



        On Mon, Apr 24, 2017 at 9:46 PM, Philip McGrath
        <phi...@philipmcgrath.com <mailto:phi...@philipmcgrath.com>> wrote:

            Another thing that might be relevant:

                In contrast, direct assignment to a parameter (by
                calling the parameter procedure with a value) changes
                the value in a thread cell, and therefore changes the
                setting only for the current
                thread. http://docs.racket-lang.org/reference/parameters.html
                <http://docs.racket-lang.org/reference/parameters.html>


            Also, do you want multiple underlying connections of the
            same virtual connection to potentially use different values
            for the user/database/password/port?


        Nope, a given dbh should only be connected to one DB.




            If not, the most recent way I've dealt with this sort of
            thing is to come up with some type of "database connection
            spec" — for a quick example, I'll use a hash table — and
            then do some things with parameter guards, opaque
            structures, and a tiny macro to keep the details away from
            higher-level code.

            I think code might be clearer than a description, so here's
            a minimal sketch:


        I admit that I had to stare at this for a few minutes and look
        up some details (e.g. how hash/dc works), but when it clicked my
        reaction was "Racket. Is. Epic."

        Thank you for this; it's exactly what I needed and I'll
        definitely use it.  Two questions:

        1) This line was intended to reference
        (current-database-handle), not (current-database), right?
            [_ (force (database-handle-promise (current-database)))]))

        2) We're already using hashes for data passing in a lot of areas
        because (a) they are really convenient and (b) my co-founder and
        I both come from Perl backgrounds.  In this particular case,
        though, it seems like maybe I should define a struct for the
        database spec instead of a hash, as hash/dc validates the keys
        and values that happen to exist in the hash, as opposed to
        validating that the hash has a given set of keys/values.  Does
        this make sense or am I overcomplicating things?

        Dave

        NB:  I'm glad you used hashes for the demo, though, as it was
        much easier to follow and introduced me to the hash/dc contract.


            #lang racket

            (require db
                     )

            (struct database-handle (promise))

            (define database-spec/c
              (or/c database-handle?
                    (hash/dc [k (or/c 'user 'database 'password 'port)]
                             [v (k)
                                (case k
                                  [(user database password)
                                   string?]
                                  [(port)
                                   natural-number/c]
                                  [else none/c])])))

            (define default-database-spec
              #hasheq([user . "postgres"]
                      [database . "test-db"]
                      [password . "mellon"]
                      [port . 5433]))


            (define/contract current-database-handle
              (parameter/c database-spec/c database-handle?)
              (let ([initialize
                     (λ (spec)
                       (if (database-handle? spec)
                           spec
                           (let ([u (hash-ref spec 'user)]
                                 [d (hash-ref spec 'database)]
                                 [p (hash-ref spec 'password)]
                                 [prt (hash-ref spec 'port)])
                             (database-handle
                              (delay (virtual-connection
                                      (connection-pool
                                       (λ ()
                                         (postgresql-connect
                                          #:user u
                                          #:database d
                                          #:password p
                                          #:port prt
                                          )))))))))])
                (make-parameter (initialize default-database-spec)
                                initialize)))

            (define-syntax db
              (syntax-id-rules ()
                [_ (force (database-handle-promise (current-database)))]))

            (provide database-handle? ;keep constructor & accessor private
                     database-spec/c
                     current-database-handle
                     db
                     )



            On Mon, Apr 24, 2017 at 7:19 PM, David Storrs
            <david.sto...@gmail.com <mailto:david.sto...@gmail.com>> wrote:



                On Mon, Apr 24, 2017 at 4:43 PM, Scott Moore
                <sc...@thinkmoore.net <mailto:sc...@thinkmoore.net>> wrote:

                    Parameters are thread local, and from reading the
                    docs of virtual-connection and connection-pool, I
                    think they create new threads to handle the
                    connections. These threads are probably created
                    before you parameterize, and thus see the original
                    values.

                    Try replacing the virtual-connection stuff with a
                    single call to do-connect, and see if that works.
                    (Obviously not the real fix, but may help identify
                    the error).


                This was exactly it.  Thanks, Scott.

                Now I just need to figure out how to work around it.


                    On Apr 24, 2017, 4:35 PM -0400, David Storrs
                    <david.sto...@gmail.com
                    <mailto:david.sto...@gmail.com>>, wrote:
                    I'm finding parameters confusing and was hoping
                    someone could explain them for me.  The following
                    is a simplified version of our production code;
                    I'm expecting it to fail but it does not.  What am
                    I not understanding?

                    The sequence goes:

                    *) db.conf creates and provides some parameters
                    (db username, db password, db name, db port) and a
                    function for creating database connections using
                    those parameters

                    *) dbstuff.rkt requires db.conf, re-provides the
                    parameters from db.conf, and also provides a
                    wrapper around the db connector from db.conf

                    *) bar.rkt parses the command line, uses that
                    command line data to set the parameters that were
                    imported from db.conf by way of dbstuff.rkt and
                    makes a DB call

                    *) I run bar.rkt from the command line with an
                    invalid username for the database, expecting it to
                    fail to connect, but it works fine???



                    My first guess was that since I was using the
                    parameters as default values perhaps they were
                    being evaluated at compile time instead of
                    runtime.  I tried changing the default value to #f
                    and then doing (or u (db-user)) in the body of the
                    function, but that had no effect.

                    I am utterly baffled at this point.  Any help
                    would be much appreciated.



                    ################################# file: "db.conf"
                    #lang racket

                    (require db)

                    (define db-user     (make-parameter "postgres"))
                    (define db-name     (make-parameter "test-db"))
                    (define db-password (make-parameter "mellon"))
                    (define db-port-num (make-parameter 5433))

                    (define (do-connect #:u [u (db-user)]
                                        #:d [d (db-name)]
                                        #:p [p (db-password)]
                                        #:prt [prt (db-port-num)])
                      (println (~a "In db.conf db-user: " (db-user)))
                      (println (~a "db-name: " (db-name)))
                      (println (~a "db-password: " (db-password)))
                      (println (~a "db-port: " (db-port-num)))

                      (postgresql-connect #:user     u
                                          #:database d
                                          #:password p
                                          #:port     prt
                                          ))

                    (define dbh (virtual-connection
                                 (connection-pool
                                  do-connect)))

                    (define (make-connect)
                      dbh)

                    (provide (all-defined-out))


                    ################################# file: "dbstuff.rkt"
                    #lang racket

                    (require db)
                    (require "db.conf")

                    (define/contract (dbh)
                      (-> connection?)
                      (make-connect))

                    (provide (all-defined-out)
                             (all-from-out db)
                             (all-from-out "db.conf"))


                    ################################# file: "bar.rkt"
                    #lang racket

                    (require "dbstuff.rkt")

                    (command-line
                     #:program "db demo"
                     #:once-each
                     [("--db-user") dbu "Username for database handles"
                      (db-user dbu)]
                     )

                    (println (~a "In bar: db user: " (db-user)))
                    (query-value (dbh) "select 'this should not have
                    worked'")


                    ################################# command line:

                    $ racket bar.rkt --db-user invalid_user
                    "In bar: db user: invalid_user"
                    "In db.conf db-user: postgres"
                    "db-name: test-db"
                    "db-password: mellon"
                    "db-port: 5433"
                    "this should not have worked"




                    --
                    You received this message because you are
                    subscribed to the Google Groups "Racket Users" group.
                    To unsubscribe from this group and stop receiving
                    emails from it, send an email to
                    racket-users+unsubscr...@googlegroups.com
                    <mailto:racket-users+unsubscr...@googlegroups.com>.
                    For more options, visit
                    https://groups.google.com/d/optout
                    
<https://urldefense.proofpoint.com/v2/url?u=https-3A__groups.google.com_d_optout&d=CwMFaQ&c=WO-RGvefibhHBZq3fL85hQ&r=OPR-Xys5wfSBIeTkWaH0D_htBR-X7qY24pTHU6ib2iM&m=Lcp1Jw9QrhH7FcWlq8qeLkEBKViDQXsoSPmFvPX80kw&s=VCtNBj2gcTR_1WATmbFxyRwK11FRxpWqOxTfpfzQYNA&e=>.


                --
                You received this message because you are subscribed to
                the Google Groups "Racket Users" group.
                To unsubscribe from this group and stop receiving emails
                from it, send an email to
                racket-users+unsubscr...@googlegroups.com
                <mailto:racket-users+unsubscr...@googlegroups.com>.
                For more options, visit
                https://groups.google.com/d/optout
                <https://groups.google.com/d/optout>.



        --
        You received this message because you are subscribed to the
        Google Groups "Racket Users" group.
        To unsubscribe from this group and stop receiving emails from
        it, send an email to racket-users+unsubscr...@googlegroups.com
        <mailto:racket-users+unsubscr...@googlegroups.com>.
        For more options, visit https://groups.google.com/d/optout
        <https://groups.google.com/d/optout>.



--
You received this message because you are subscribed to the Google
Groups "Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to racket-users+unsubscr...@googlegroups.com
<mailto:racket-users+unsubscr...@googlegroups.com>.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Racket 
Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to