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>
wrote:

>
>
> On Mon, Apr 24, 2017 at 9:46 PM, Philip McGrath <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-lan
>>> g.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>
>> wrote:
>>
>>>
>>>
>>> On Mon, Apr 24, 2017 at 4:43 PM, Scott Moore <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>,
>>>> 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.
>>>> 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.
>>> 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.
>

-- 
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