[Cl-perec-devel] Lots of questions :-)

Mihai Bazon mihai at bazon.net
Wed Sep 16 06:01:17 UTC 2009

Hey Pixel!

Thanks for your clarifications and also for your blog series. ;-)

The defmethod stuff works, and you guys convinced me -- it's better
than using an SQL writer.

I have one more question: is there a way to use objects without
"reviving instance"?  The reason I'd do that is that there are objects
that change very infrequently, such as Users.  If I get an User object
from the DB when a request comes in and I need to pass it around to
various functions, it would be nice if they don't need to do
(with-transaction (revive-instance user)) because that involves a
(mostly useless) SELECT since the data is already there.

I noticed (setf (cl-perec:persistent-p user) nil) but that feels like a
dirty hack; will this save the object back in the DB if I need to
change it?


Pixel // pinterface <pinterface at gmail.com> wrote:
> Disclaimer: It's been nearly two months since I've done anything with
> cl-perec (too many other projects, bleh).  I've done some minimal testing
> of what I say below, so hopefully I'm not committing any major errors,
> but there's no guarantee. :)
> "Mihai Bazon" <mihai at bazon.net> wrote in
> message news:f33c75c11638fc18c81f7cb445e6924c at bazon.net...
> > Hi Attila,
> >
> > Thanks for your fast reply!
> >
> > Indeed, I knew about pinterface series, it helped a lot to get started.
> I'm glad to hear that.[0]  Feel free to let me know if anything was unclear,
> poorly explained, or seemed to be missing.  I generally know what I mean,
> but that doesn't mean anybody else does! :)
> > I tried defining my own type today and after some embarrassing amount of
> > trial and error I came up with this:
> >
> > (cl-perec:defptype password ()
> >   '(text 128))
> (Aside: I somehow missed 'cl-perec:text entirely.  Good to know about that.)
> > (cl-perec::defmapping password (cl-rdbms::sql-character-varying-type ...)
> >   'cl-perec:identity-reader             ; unexported
> >   'password-writer)
> >
> > (defun password-writer (val rdbms-values index)
> >   (setf (elt rdbms-values index)
> >         (ironclad:byte-array-to-hex-string
> >          (ironclad:digest-sequence :sha1 (babel:string-to-octets val)))))
> >
> > (pushnew 'password cl-perec::*canonical-types*)
> > (pushnew 'password cl-perec::*mapped-type-precedence-list*)
> >
> > It works, it creates SHA1 automatically for password fields, but there's
> > one weird side effect: now *all* columns of a TEXT type are
> > VARCHAR(128), and they are all mangled to SHA1. :-) Could you point out
> > what did I do wrong?
> The way Common Lisp types work, (subtypep 'password 'text) and (subtypep
> 'text 'password) are both true.  Not coincidentally, one of the tests
> cl-perec uses to determine which reader/writer mapping to use is
> #'subtypep[1].  Since--according to the CL type hierarchy--your 'password
> type and perec's 'text types are equivalent, whichever appears in
> *mapped-type-precedence-list* first will determine behavior for both (and
> any other equivalent types).
> You can generally trip up #'subtypep using a satisfies test.  For instance:
>   (cl-perec:defptype password ()
>     '(and (text 128) (satisfies constantly)))
> Will result in[2]
>   (subtypep 'password 'text) => t, t
>   (subtypep 'text 'password) => nil, nil
> which means so long as 'password appears in *mapped-type-precedence-list*
> /before/ 'text, the 'password reader/writer mapping will be used for
> passwords, but not text.  If 'text appears before 'password, 'password will
> be run through 'text's reader/writer mapping.
> HOWEVER!  As Attila has already mentioned, the SQL writer is the wrong place
> to encrypt passwords anyway.  I /think/[3] you could do something like:
>   (defmethod (setf password-of) (new-value object)
>     (setf (slot-value object 'password) (encrypt-password new-value)))
> or even
>   (defmethod (setf password-of) (new-value object)
>     (call-next-method (encrypt-password new-value) object))
> with the caveat that you would always have to update the password using the
> accessor function, and never using slot-value or with-slots (unless you
> wanted to bypass the encryption for some reason).
> Attila already covered this somewhat, but it bears repeating: the SQL-reader
> and SQL-writer for a type must be the inverse of each other.  In essence,
>   (equivalent-p thing (sql-reader (sql-writer thing)))
> should be true (for whatever value of #'equivalent-p is relevant to your
> type).  Any transformation you make in the sql-writer must be undone by the
> sql-reader.
> In general, the only time you should be defining your own SQL readers and
> writers is when you want to serialize an object in a funky way.  E.g., if
> you want to convert objects of an 'e-mail class into strings of the form
> <local-part>@<domain.tld> for the SQL database.
> Footnotes:
> 0. Hi!  I'm the guy who is writing that particular series on cl-perec. :)
> 1. see #'cl-perec::mapped-type-for in persistence/type.lisp
> 2. If you redefine the password type within the same lisp image, be sure to
>    remove 'password from the cl-perec::*mapped-types* hash, otherwise
>    cl-perec won't register the change in type hierarchy.[4]
> 3. "think" here meaning "Warning!  I haven't bothered to check!"
> 4. If it still doesn't register the change, restart your lisp image.  perec
>    does a lot of aggressive internal caching and gets tripped up by changes
>    a little too easily.
> Hopefully that wasn't too confusing.
> -pix (aka pinterface)
> _______________________________________________
> Cl-perec-devel mailing list
> Cl-perec-devel at common-lisp.net
> http://common-lisp.net/cgi-bin/mailman/listinfo/cl-perec-devel

More information about the cl-perec-devel mailing list