Skip to content

Commit

Permalink
Merge pull request #252 from sabracrolleton/master
Browse files Browse the repository at this point in the history
Resolve Issues 250 and 251
  • Loading branch information
sabracrolleton authored Oct 12, 2020
2 parents 731fdcf + e0a68f5 commit 9be85f3
Show file tree
Hide file tree
Showing 11 changed files with 744 additions and 227 deletions.
4 changes: 2 additions & 2 deletions cl-postgres.asd
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
:author "Marijn Haverbeke <[email protected]>"
:maintainer "Sabra Crolleton <[email protected]>"
:license "zlib"
:version "1.32"
:version "1.32.3"
:depends-on ("md5" "split-sequence" "ironclad" "cl-base64" "uax-15"
(:feature (:or :sbcl :allegro :ccl :clisp :genera
:armedbear :cmucl)
:armedbear :cmucl :lispworks)
"usocket")
(:feature :sbcl (:require :sb-bsd-sockets)))
:components
Expand Down
74 changes: 63 additions & 11 deletions doc/postmodern.org
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,13 @@ Immediately commit an open transaction.
:CUSTOM_ID: 58a7459f-a1f4-4797-83b5-8b5257ca2cf7
:END:

Roll back the given transaction.
Roll back the given transaction, but the transaction
block is still active. Thus calling abort-transaction in the middle of a
transaction does not end the transaction. Any subsequent statements will still
be executed. Per the Postgresql documentation: ABORT rolls back the current
transaction and causes all the updates made by the transaction to be discarded.
This command is identical in behavior to the standard SQL command ROLLBACK, and
is present only for historical reasons..

** macro with-savepoint (name &body body)
:PROPERTIES:
Expand Down Expand Up @@ -1091,7 +1097,20 @@ The class must be quoted.
:END:
→ list

Returns list of values that are the primary key of dao.
Returns list of values that are the primary key of dao. Explicit keys takes
priority over col-identity which takes priority over col-primary-key.

This is likely interesting if you have primary keys which are composed of
more than one slot. Pay careful attention to situations where the primary key
not only has more than one column, but they are actually in a different order
than they are in the database table itself. Obviously the table needs to have
been defined. You can provide a quoted class-name or an instance of a dao.

** method find-primary-key-column
→ symbol

Loops through a class's column definitions and returns the first column name
that has bound either col-identity or col-primary-key.

** method dao-exists-p (dao)
:PROPERTIES:
Expand All @@ -1114,6 +1133,26 @@ unbound.
Combines make-instance with insert-dao. Make the instance of the given class and
insert it into the database, returning the created dao.

** method fetch-defaults (dao)

→ dao if there were unbound slots with default values, otherwise nil

Used to fetch the default values of an object on creation.
An example would be creating a dao object with unbounded slots.
Fetch-defaults could then be used to fetch the default values from the database
and bind the unbound slots which have default values. E.g.
#+BEGIN_SRC lisp
(let ((dao (make-instance 'test-data :a 23)))
(pomo:fetch-defaults dao))
#+END_SRC
** method find-primary-key-column (class)

→ symbol

Loops through a class's column definitions and returns
the first column name that has bound either col-identity or col-primary-key.
Returns a symbol.

** macro define-dao-finalization (((dao-name class) &rest keyword-args) &body body)
:PROPERTIES:
:ID: 645a03ec-739a-4ee5-b83d-dcbe43ef009a
Expand Down Expand Up @@ -1283,11 +1322,15 @@ columns. Raises an error when no row matching the dao exists.
:END:
→ boolean

Tries to insert the given dao using insert-dao. If this raises a unique key
violation error, it tries to update it by using update-dao instead. Be aware
that there is a possible race condition here ― if some other process deletes the
row at just the right moment, the update fails as well. Returns a boolean
telling you whether a new row was inserted.
Tries to insert the given dao using insert-dao. If the dao has unbound slots,
those slots will be updated and bound by default data triggered by the
database. If this raises a unique key violation error, it tries to update it by
using update-dao instead. In this case, if the dao has unbound slots, updating
will fail with an unbound slots error.

Be aware that there is a possible race condition here ― if some other process
deletes the row at just the right moment, the update fails as well. Returns a
boolean telling you whether a new row was inserted.

This function is unsafe to use inside of a transaction ― when a row with the
given keys already exists, the transaction will be aborted. Use
Expand All @@ -1304,10 +1347,13 @@ See also: upsert-dao.

The transaction safe version of save-dao. Tries to insert the given dao using
insert-dao. If this raises a unique key violation error, it tries to update it
by using update-dao instead. Be aware that there is a possible race condition
here ― if some other process deletes the row at just the right moment, the
update fails as well. Returns a boolean telling you whether a new row was
inserted.
by using update-dao instead. If the dao has unbound slots, updating will fail
with an unbound slots error. If the dao has unbound slots, those slots will be
updated and bound by default data triggered by the database.

Be aware that there is a possible race condition here ― if some other process
deletes the row at just the right moment, the update fails as well. Returns a
boolean telling you whether a new row was inserted.

Acts exactly like save-dao, except that it protects its attempt to insert the
object with a rollback point, so that a failure will not abort the transaction.
Expand Down Expand Up @@ -1347,6 +1393,12 @@ actually indicate the existence of record in the database.
This method returns two values: the DAO object and a boolean (T if the object
was inserted, NIL if it was updated).

IMPORTANT: This is not the same as insert on conflict (sometimes called an upsert)
in Postgresq. An upsert in Postgresql terms is an insert with a fallback of updating
the row if the insert key conflicts with an already existing row. An upsert-dao
in Postmodern terms is the reverse. First you try updating an existing object. If
there is no existing object to oupdate, then you insert a new object.

** method delete-dao (dao)
:PROPERTIES:
:ID: f3371904-cd84-4392-a301-0f910bcf1b90
Expand Down
2 changes: 1 addition & 1 deletion postmodern.asd
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
:maintainer "Sabra Crolleton <[email protected]>"
:homepage "https://github.com/marijnh/Postmodern"
:license "zlib"
:version "1.32.1"
:version "1.32.3"
:depends-on ("alexandria"
"cl-postgres"
"s-sql"
Expand Down
2 changes: 2 additions & 0 deletions postmodern/package.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#+postmodern-use-mop
(:export
#:dao-class #:dao-exists-p #:dao-keys #:query-dao #:select-dao #:get-dao
#:fetch-defaults
#:do-query-dao #:do-select-dao
#:with-column-writers
#:insert-dao #:update-dao #:save-dao #:save-dao/transaction #:upsert-dao
Expand Down Expand Up @@ -149,6 +150,7 @@
#:list-foreign-keys
#:list-unique-or-primary-constraints
#:find-primary-key-info
#:find-primary-key-column
;; roles
#:list-roles
#:list-role-permissions
Expand Down
4 changes: 2 additions & 2 deletions postmodern/prepare.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,15 @@ Note that it will attempt to automatically reconnect if database-connection-erro
or admin-shutdown. It will reset prepared statements triggering an
invalid-sql-statement-name error. It will overwrite old prepared statements
triggering a duplicate-prepared-statement error."
`(let ((overwrite t))
`(let ((overwrite ,*allow-overwriting-prepared-statements*))
,(generate-prepared '(lambda) (next-statement-id) query format)))

(defmacro defprepared (name query &optional (format :rows))
"This is the macro-style variant of prepare. It is like prepare, but gives the
function a name which now becomes a top-level function for the prepared
statement. The name should not be a string but may be quoted."
(when (consp name) (setf name (s-sql::dequote name)))
`(let ((overwrite t))
`(let ((overwrite ,*allow-overwriting-prepared-statements*))
,(generate-prepared `(defun ,name) name query format)))

(defmacro defprepared-with-names (name (&rest args)
Expand Down
Loading

0 comments on commit 9be85f3

Please sign in to comment.