- Connections
- Model types
- Basic model management
- Model CRUD generic functions
- Model transactions
- JSON helper functions and specials
- User queries and JSON syntactic sugar for S-SQL
- Model customization generic functions
- Further model management
- Postgres backend
- Postmodern isolation level transactions
- lparallel support (optional)
Dynamic variable
Set this to a list congruent with the parameters expected by POSTMODERN:CONNECT-TOPLEVEL, for use by the testing and example code.
Function
&optional (connect-spec *postmodern-connection*)
Ensure a Postmodern top level connection is active by applying the contents of the list CONNECT-SPEC to POMO:CONNECT-TOPLEVEL.
Class
The Postgres-JSON model base class supported by implementation and interface methods for storing, querying and modifying JSON documents in a Postgres database.
Class
A Postgres-JSON model that maintains a history of previous values of updated or deleted documents.
Class
A Postgres-JSON model that consists of JSON documents having an object root node.
Class
A Postgres-JSON model that maintains history and consists of JSON documents having an object root node.
Macro
name constant (&rest superclasses)
Define a new class named NAME, a symbol, having SUPERCLASSES, all symbols. Define a global variable named CONSTANT, a symbol, with value an instance of the new class.
Generic function
model
Call CREATE-BACKEND on MODEL unless said backend already exists.
Generic function
model
Drop the Postgres backend of MODEL. This will irrevocably delete all data associated with the model.
Generic function
model object &optional key
Insert lisp object OBJECT into the backend MODEL, after JSON serialization. If KEY is supplied use that as the primary key for the JSON document rather than an automatically generated one. Return the new primary key.
Generic function
model key object
Replace the current value of the JSON document having primary key KEY in MODEL with the JSON serialization of lisp object OBJECT. Return KEY on success, NIL if no such KEY is found.
Method
(model pgj-history-model) key object
As per SUPERSEDE but keep a separate record of all previous rows.
Generic function
model key
If there is a JSON document with primary key KEY in MODEL return the result of deserializing it. Otherwise return NIL.
Generic function
model
Return as a list the result of deserializing all JSON documents in MODEL.
Generic function
model key
Delete the JSON document with primary key KEY from MODEL. Return KEY on success, NIL if no such KEY exists.
Method
(model pgj-history-model) key
As per EXCISE but keep a separate record of all deleted rows.
Generic function
model
Delete all JSON documents in MODEL. Returns the number of documents deleted.
Method
(model pgj-history-model)
As per EXCISE-ALL but keep a separate record of all deleted rows.
Generic function
model
Return two values: a list of all primary keys for MODEL and the length of that list.
Generic function
model
Return the count of all JSON documents in MODEL.
Generic function
model property
Return the result of deserializing all JSON documents in MODEL which have a top level object property PROPERTY, a string, or if said string appears as an element of a top level array. This is in the Postgres operator ? sense. Requires a Postgres GIN index with operator class :jsonb-ops defined on MODEL.
Generic function
model property
Return all distinct values of the top level PROPERTY, a string, in all of the JSON documents of MODEL. JSON deserialization is performed by funcalling *FROM-JSON*. Note that this is not a prepared query so care must be taken that PROPERTY is sanitized if it derives from arbitrary user input.
Generic function
model &key contains
Filter all JSON documents in MODEL by checking they 'contain', in the Postgres @> operator sense, the object CONTAINS which will be serialized to a JSON document by funcalling *TO-JSON*. If CONTAINS is NIL, apply no containment restriction.
Method
(model pgj-object-model) &key contains properties limit
Filter all JSON documents in MODEL as follows. Each document must 'contain', in the Postgres @> operator sense, the object CONTAINS which will be serialized to a JSON document by funcalling *TO-JSON*. If CONTAINS is NIL, apply no containment restriction. PROPERTIES may be a list of strings being properties in the top level of the JSON documents in MODEL and only the values of said properties will be returned, bundled together in a JSON document. If PROPERTIES is NIL the entire JSON document will be returned. LIMIT, if supplied, must be an integer that represents the maximum number of objects that will be returned. If properties is NIL JSON deserialization is performed by DESERILIZE, otherwise by funcalling *FROM-JSON*. Note that this is not a prepared query so extra care must be taken if PROPERTIES or CONTAIN derive from unsanitized user input.
Generic function
model key &key
Return a list of the result of deserializing all previous values of the JSON document with primary key KEY in MODEL.
Method
(model pgj-history-model) key &key (validity-keys-p t) (valid-from-key "_validFrom") (valid-to-key "_validTo")
Return a list of the result of deserializing all previous values of the JSON document with primary key KEY in MODEL, in chronological order. If VALIDITY-KEYS-P is true, include the 'valid_from' and 'valid_to' Postgres timestamps for the historical document as properties in the top level JSON object --- it must be an object in this case. VALID-FROM-KEY and VALID-TO-KEY are strings that will be the property names of the respective timestamps.
Macro
(&optional name) &body body
Evaluate BODY inside a Postgres transaction using the 'repeatable read' isolation level in read/write mode. Retry any serialization failures although chronic incidence will still result in the client seeing CL-POSTGRES-ERROR:SERIALIZATION-FAILURE conditions --- see also *SERIALIZATION-FAILURE-SLEEP-TIMES*. Implemented using Postmodern WITH-LOGICAL-TRANSACTION so may be nested. NAME can be used with Postmodern's abort-transaction and commit-transaction. NAME should not be a Postgres reserved word. Ideal for any group of mutating model interface functions.
Function
name
If this is the root node of a nested set of WITH-MODEL-TRANSACTIONs then 'rollback' the transaction NAME. Otherwise rollback to the Postgres savepoint NAME.
Function
name
If this is the root node of a nested set of WITH-MODEL-TRANSACTIONs then 'commit' the transaction NAME. Otherwise merely release the savepoint NAME.
Dynamic variable
'(0 1 2 4 7)
The length of this list of real numbers determines the number of times to retry when a Postgres transaction COMMIT sees a CL-POSTGRES-ERROR:SERIALIZATION-FAILURE condition. For each retry we sleep the duration specified plus a random number of milliseconds between 0 and 2000. However, if 0 sleep is specified, we do not sleep at all. If set to NIL no condition handling is performed hence the client will always see any such serialization failures.
Function
&rest args
Return an 'equal key/value hash-table consisting of pairs of ARGS. For JSON use your keys must be Common Lisp strings.
Function
object &key (stream *standard-output*) (indent 4)
Pretty print lisp OBJECT as JSON to STREAM with specified INDENT.
Dynamic variable
#'to-json
A function designator for a function of one argument which serializes a lisp object to a JSON string.
Dynamic variable
#'from-json
A function designator for a function of one argument which returns the result of parsing the JSON string being its input.
Macro
name (&rest query-params) &body query
Define a Postmodern S-SQL based QUERY with name NAME, a symbol. QUERY may use the macro forms j->, j->> jbuild and to-json, documented separately. Elements of QUERY-PARAMS may be symbols, the number and order of said symbols serving to define the parameters the query will be supplied with at run time. Additionally, any occurence of a symbol from the QUERY-PARAMS list in the QUERY from proper will be replaced with '$1, '$2 etc. as appropriate based on the order of QUERY-PARAMS. In this way your queries may use named parameters, but this is not mandatory.
Furthermore, a la cl-ppcre:register-groups-bind
, any element of the
QUERY-PARAMS list may itself be a list of the form
(function-designator &rest params) in which case the PARAMS are
still treated as parameters, in order, but at run time
FUNCTION-DESIGNATOR is called on each of the actual arguments of the
PARAMS to transform said arguments before use by the underlying query.
For example (foo (\*to-json\* bar baz) blot)
is an acceptable
QUERY-PARAMS list, as long as *to-json* is funcallable. bar and baz
will be replaced by the result of funcalling *to-json* on them,
repectively.
The Postmodern result format is always :column
and so you must
ensure that each row produces just a single datum, being a valid
Postgres JSON type. In practice this means either i) returning the
column named jdoc
in any model, which is the entire JSON document,
or ii) using the jbuild
macro to build a JSON object on the fly.
Macro
form1 &optional form2
S-SQL syntactic sugar to turn a single string FORM1 into a Postgres -> operation using the default JSON column 'jdoc and the property FORM1; or to turn a symbol FORM1 and string FORM2 into a -> operation using the 'jdoc JSON column in table FORM1 and the property FORM2.
Macro
form1 &optional form2
S-SQL syntactic sugar to turn a single string FORM1 into a Postgres ->> operation using the default JSON column 'jdoc and the property FORM1; or to turn a symbol FORM1 and string FORM2 into a ->> operation using the 'jdoc JSON column in table FORM1 and the property FORM2.
Macro
form
S-SQL syntactic sugar to cast FORM to the Postgres jsonb type.
Macro
&rest key-forms
S-SQL syntactic sugar to create a new Postgres JSON object from the
KEY-FORMS. Each KEY-FORM is a list. In the simplest and first case
it may be a list of strings, said strings indicating properties of the
top level JSON object in the 'jdoc column of the query; the properties
and their values will be returned by JBUILD, in a fresh JSON object.
In the second case the list may start with a symbol (or a quoted
symbol) in which case the following strings indicate properties of
the top level JSON document in the 'jdoc column in the DB table named
by the symbol. Now, a la with-slots
, each string in the list may
itself be replaced by a list of two strings, the first being the
resulting property name in the object returned by JBUILD, the second
being the accessor property for the top level JSON object in the 'jdoc
column. This flexibility is required because we are building a JSON
object and cannot have duplicate properties so if we need the "id"
property from both a cat
and a dog
model, one of them needs to be
relabeled.
Generic function
model
The name, a symbol, of a Postgres sequence to provide primary keys upon insertion of fresh documents into a backend model. May be NIL, in which case explicit primary keys must be supplied for all inserts.
Generic function
model
The name, a symbol, for the primary key column in backend model tables.
Generic function
model
The name, a symbol, for the Postgres type of the primary key column in the backend model tables. KEY arguments to model interface methods must be compatible with this type.
Generic function
model
The name, a keyword, for the initial Postgres GIN operator class to use for the model's GIN index. See also USE-GIN-INDEX. If NIL, make no GIN index.
Generic function
model object
Serialize lisp OBJECT to a form suitable for storage as a JSON document in backend MODEL. Return same. Called by INSERT, for example, to convert Lisp objects to JSON before DB insertion proper.
Generic function
model jdoc
Deserialize the string JDOC from MODEL's backend to a lisp object. Return same. Called by FETCH, for example, to convert JSON strings from the backend into Lisp objects.
Generic function
model object key
Called before SERIALIZE which is called before document inserts or updates. An opportunity to modify the lisp OBJECT using the intended/current primary KEY of the JSON document in the MODEL's backend.
Method
(model pgj-model) object key
Do nothing and return OBJECT.
Method
(model pgj-object-model) (object hash-table) key
Destructively modify hash-table OBJECT by assigning the value KEY to a key named by the downcased symbol name of MODEL-KEY-NAME of MODEL. Returns the modified OBJECT.
Generic function
model
Create the backend tables and indexes for a MODEL.
Generic function
model
Return true if MODEL has a Postgres backend, NIL otherwise.
Condition
Signaled to prevent accidental deletion of database assets such as tables or schema.
Function
condition
Invoke a 'REALLY-DO-IT restart.
Dynamic variable
'(:jsonb-ops :jsonb-path-ops)
A list of keywords representing Postgres GIN operator classes.
Generic function
model gin-operator-class
Create a Postgres GIN index for MODEL using GIN-OPERATOR-CLASS, a keyword that must be a member of *gin-operator-classes*. First drop any existing GIN index.
Dynamic variable
'pgj-model
A symbol being the name of the Postgres schema created to house all database backend objects.
Function
Drop the entire Postgres schema *PGJ-SCHEMA* in the database Postmodern is currently connected to. This will irrevocably delete ALL your data in ALL your models so it uses a RESTART-CASE to guard against human error.
Dynamic variable
(format nil "~A,public" (to-sql-name *pgj-schema*))
The default value used by ALTER-ROLE-SET-SEARCH-PATH.
Function
user &optional (search-path *default-search-path*)
Alter the role of Postgres user USER, a string, to set the 'search_path' setting to the string SEARCH-PATH. In most cases this is what you want so than when defining your own queries with DEFINE-MODEL-QUERY unqualified relation names can be found in our default schema (which is not the PUBLIC schema). This setting does not effect the normal model interface functions such as FETCH and FILTER as they use fully qualified table names at all times. Will only take effect upon your next connection. Beware, may be overridden by settings in your ~/.psqlrc file. See also the Postgres documentation on search paths and settings.
Function
sequence &optional (schema *pgj-schema*)
Create a PostgreSQL sequence with name SEQUENCE in SCHEMA (both symbols). Requires an active DB connection.
Function
If you get a 'Database error 26000: prepared statement ... does not exist error' while mucking around at the REPL, call this. A similar error in production code should be investigated.
Dynamic variable
'+repeatable-read-rw+
The isolation level, a symbol, to use for WITH-MODEL-TRANSACTION. For models that maintain history can only be +REPEATABLE-READ-RW+ or +SERIALIZABLE-RW+. For models without history could conceivably be +READ-COMMITTED-RW+.
Condition
Signaled for a nested invocation of WITH-ENSURED-TRANSACTION-LEVEL or WITH-LOGICAL-TRANSACTION-LEVEL inside a previous invocation with an incongruent isolation level.
Constant
"isolation level serializable read write"
START TRANSACTION string to set Postgres 'Serializable' isolation level and read/write.
Constant
"isolation level repeatable read read write"
START TRANSACTION string to set Postgres 'Repeatable read' isolation level and read/write.
Constant
"isolation level read committed read write"
START TRANSACTION string to set Postgres 'Read committed' isolation level, which is the default, and read write.
Constant
"isolation level read committed read only"
START TRANSACTION string to set Postgres 'Read committed' isolation level, which is the default, and read only.
Macro
(name isolation-level) &body body
Unilaterally evaluate BODY inside a Postmodern WITH-TRANSACTION form with Postgres 'transaction mode' set to the symbol-value of ISOLATION-LEVEL, a symbol. The symbol NAME is bound to the Postmodern `transaction-handle' and may be used in calls to Postmodern's abort-transaction and commit-transaction.
Macro
(name isolation-level) &body body
Similar to Postmodern's WITH-LOGICAL-TRANSACTION but start any top
level transaction with Postgres 'transaction mode' set to the
symbol-value of ISOLATION-LEVEL. The symbol NAME is bound to the
Postmodern transaction-handle' and may be used in calls to Postmodern's abort-transaction and commit-transaction. The condition
incompatible-transaction-setting' will be signaled for incongruent
nested isolation levels.
Macro
(isolation-level) &body body
Similar to Postmodern's ENSURE-TRANSACTION but start any top level transaction with Postgres 'transaction mode' set to the symbol-value of ISOLATION-LEVEL. The condition `incompatible-transaction-setting' will be signaled for incongruent nested isolation levels.
Dynamic variable
An lparallel kernel to manage worker threads. Typically bound to the result of MAKE-PGJ-KERNEL for use by interface calls such WITH-CONNECTED-THREAD.
Dynamic variable
Thread local Postmodern database connection.
Function
connect-spec &optional (n 4)
Make an lparallel kernel object where each worker thread is given a permanent DB connection, made using a Postmodern CONNECT-SPEC, a list. Start N workers. Ensure your Postgres can handle at least N concurrent connecions.
Function
End the lparallel kernel in *PGJ-KERNEL*.
Function
function
Ask that an lparallel worker perform FUNCTION, a function, given a current Postmodern DB connection. Block until the result is received and return it. *PGJ-KERNEL* must be bound to the result of MAKE-PGJ-KERNEL.
Macro
nil &body body
Wrap BODY in a lambda and invoke CALL-WITH-CONNECTED-THREAD. *PGJ-KERNEL* must be bound to the result of MAKE-PGJ-KERNEL.
Dynamic variable
A single lparallel channel for submitting tasks via SUBMIT-PGJ-TASK and receiving results via RECEIVE-PGJ-RESULT.
Function
Make an lparallel channel. *PGJ-KERNEL* must be bound to the result of MAKE-PGJ-KERNEL.
Function
function
Submit the function FUNCTION, with a Postmodern connection, as an lparallel task on our channel *PGJ-CHANNEL*. *PGJ-KERNEL* must be bound to the result of MAKE-PGJ-KERNEL.
Macro
nil &body body
Wrap BODY in a lambda and call SUBMIT-PGJ-FUNCTION. *PGJ-KERNEL* must be bound to the result of MAKE-PGJ-KERNEL.
Function
Call lparallel:receive-result on our *PGJ-CHANNEL*. *PGJ-KERNEL* must be bound to the result of MAKE-PGJ-KERNEL.
Function
&key timeout
Call lparallel:try-receive-result on our *PGJ-CHANNEL*, with timeout TIMEOUT, a real. *PGJ-KERNEL* must be bound to the result of MAKE-PGJ-KERNEL.