diff options
Diffstat (limited to 'src/generics.lisp')
| -rw-r--r-- | src/generics.lisp | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/src/generics.lisp b/src/generics.lisp new file mode 100644 index 0000000..59dbc12 --- /dev/null +++ b/src/generics.lisp @@ -0,0 +1,244 @@ +;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*- +(declaim (optimize (speed 0) (safety 3) (debug 3))) + +(defpackage :org-ckons-sql + (:use :cl) + (:export :id + :*project + :*table + :*where-expression + :record + :postgres-record + :record-pkg + :get-record + :get-records + :insert-record + :update-record + :delete-record + :call-pg-function + :intersect-slots + :slot-is-field-p + :simple-date-to-date + :sanitize-json + :with-database)) + +(in-package :org-ckons-sql) + +(defgeneric to-date (record date-string) + (:documentation "In PostgreSQL, a string in the format `YYYY-MM-DD +HH:MM:SS' can be used directly to populate a date field. Therefore, +this function does nothing except to put quotes arount the value.")) + +(defgeneric sanitize (record field) + (:documentation "If `field' is a lisp `nil', returns `NULL'. Also +converts datestrings to a PLSQL to_date() function call, converts +numbers to oracle-friendly representations, and puts single ticks +around string values. All munging, workarounds, hacks, etc. to make +the interface between lisp and PostgreSQL work better should be done +here.")) + +(defgeneric call-pg-function* (record) + (:documentation "Calls a Postgresql function that returns something +other than a result set that matches up with the passed-in `record' +which is always a postgresql-record, or returns nothing at all.")) + +(defgeneric field-names-string (record) + (:documentation "Builds a string of SQL query field names.")) + +(defgeneric field-values-string (record) + (:documentation "Builds a string of SQL query values to match the +field names returned by `field-names-string'.")) + +(defgeneric field-value-pairs (record) + (:documentation "Builds a string of SQL query field/value pairs.")) + +(defgeneric field-value-pairs-no-nulls (record) + (:documentation "Builds a string of SQL query field/value pairs, but +only those fields with a non-null value are included.")) + +(defgeneric field-value-list (record) + (:documentation "Builds a list of SQL query field/value pairs, but +only those fields with a non-null value are included.")) + +(defgeneric field-value-list-no-nulls (record) + (:documentation "Builds a list of SQL query field/value pairs, but +only those fields with a non-null value are included.")) + +(defgeneric field-plist-no-nulls (record) + (:documentation "Returns the non-null field slots of `record' as a +plist. Used for record serialization.")) + +(defgeneric build-where-clause (record) + (:documentation "Builds a WHERE clause from the non-nil slots in +`record'.")) + +(defgeneric intersect-slots (record slots) + (:documentation "Since the built-in `intersect' function does not +take package name prefixes into account, and since `map-slot-names' +returns slot names prefixed with the package name, this method was +written to intersect lists ignoring package prefixes.")) + +(defgeneric expand-where-expression (record where-expression) + (:documentation "All WHERE clauses must pass through this method. +If the WHERE clause contains all hard-coded values, then it will make +it through untouched. If it contains ~a placeholders, it will be +parsed and expanded using the values contained in `record'. Finally +the WHERE keyword is prepended.")) + +(defgeneric insert-query (record) + (:documentation "Builds an INSERT query to save the state of +`record' in the database. Does not include `nil' slots.")) + +(defgeneric currval-query (record) + (:documentation "Calls currval() to get the last inserted ID. This +must be called with the same database connection that performed the +prior INSERT, and `record' must have a `*table' that can be used as +the first arguemnt to pg_get_serial_sequence().")) + +(defgeneric update-query (record) + (:documentation "Builds an UPDATE query to save the state of +`record' in the database.")) + +(defgeneric select-query (record) + (:documentation "Builds a SELECT query to retrieve the state of +`record' from the database.")) + +(defgeneric delete-query (record) + (:documentation "Builds a DELETE query to to delete a `record' from +the database. CAUTION: use this only on `record' objects whose +backend tables are real tables, not inlined table functions.")) + +(defgeneric insert-record* (record) + (:documentation "Inserts the state of `record' into the database, +and fetches the newly-created ID into the `id' slot of `record'.")) + +(defgeneric update-record* (record) + (:documentation "Saves the state of `record' to the database.")) + +(defgeneric select-records-impl* (record where-clause order-by-clause) + (:documentation "Low-level function which is to be called via +`select-records'. Executes a SELECT statement with the given WHERE +clause and ORDER BY clause. Do not include the WHERE and ORDER BY +keywords in your arguments, as they will be automatically +prepended.")) + +(defgeneric select-records* (record order-by-clause) + (:documentation "High-level interface to `select-records-impl'. +Executes a SELECT statement with the given ORDER BY clause, with a +WHERE clause automatically built by AND-separating the name/values +stored in `record', and fetches the result set of the query into a +list of `record' objects. Do not include the ORDER BY keyword in +`order-by-clause', as it will be automatically prepended.")) + +(defgeneric select-records-where* (record where-expression order-by-clause) + (:documentation "High-level interface to `select-records-impl'. +Executes a SELECT statement with the given WHERE clause and ORDER BY +clause. `where-expression' is a FORMAT-style string which uses ~a for +value placeholders. The slots of `record' that correspond to the +field names in `where-expression' are used to fill in the +placeholders. Fetches the result set of the query into a list of +`record' objects. Do not include the WHERE and ORDER BY keywords in +`where-expression' and `order-by-clause', as they will be +automatically prepended.")) + +(defgeneric delete-records-impl* (record where-expression) + (:documentation "Low-level function which is to be called via +`delete-records'. Executes a DELETE statement with the given WHERE +clause. Do not include the WHERE keyword in your argument, as this +method automatically prepends it.")) + +(defgeneric delete-record* (record) + (:documentation "High-level interface to `delete-records-impl'. +Convenience function for deleting records by `id'. record' must +conform to a certain standard pattern. Executes a DELETE statement +with a WHERE clause built with `*where-expression' and `id'. Assumes +that `*where-expression' is the format string id = ~a.")) + +(defgeneric delete-records* (record) + (:documentation "High-level interface to `delete-records-impl'. +Executes a DELETE statement with a WHERE clause automatically built by +AND-separating the name/values stored in `record'.")) + +(defgeneric delete-records-where* (record where-expression) + (:documentation "High-level interface to `delete-records-impl'. +Executes a DELETE statement with the given WHERE clause. +`where-expression' is a FORMAT-style string which uses ~a for value +placeholders. The slots of `record' that correspond to the field +names in `where-expression' are used to fill in the placeholders.Do +not include the WHERE keyword in `where-expression', as it will be +automatically prepended.")) + +(defgeneric load-record (record row) + (:documentation "Populates the slots of the `record' subclass from +the fields in `row', which are usually obtained by running a SELECT +query.")) + +(defgeneric sanitize-json (record-or-service) + (:documentation "Untaints the record or service object so that it's +safe to serialize into JSON.")) + +(defgeneric insert-record (record-pkg record) + (:documentation "Inserts a new record into the table associated with +`record', using the data from `record'.")) + +(defgeneric update-record (record-pkg record) + (:documentation "Updates an existing record in the table associated +with `record', using the data from `record'.")) + +(defgeneric delete-record (record-pkg record) + (:documentation "Deletes an existing record in the table associated +with `record', using `id' and `*where-expression' from `record'.")) + +(defgeneric delete-records (record-pkg record) + (:documentation "Deletes an existing record in the table associated +with `record', using the data from `record'.")) + +(defgeneric delete-records-where (record-pkg record where-expression) + (:documentation "Updates an existing record in the table associated +with `record', using the passed-in `where-expression'.")) + +(defgeneric get-records (record-pkg record order-by-clause) + (:documentation "All-purpose method for issuing a SELECT statement +to obtain a result set of `record' objects. The instance of `record' +passed into this method is used to hold input parameters, including +the table name. Any slots that are populated with a non-nil value +will be used in the AND-separated WHERE clause. Fetches the result +set of the query into a list of `record' objects.")) + +(defgeneric get-records-where (record-pkg record where-expression order-by-clause) + (:documentation "All-purpose method for issuing a SELECT statement +to obtain a result set of `record' objects. The instance of `record' +passed into this method is used to hold input parameters, including +the table name. `where-expression' is a FORMAT-style string which +uses ~a for value placeholders. The slots of `record' that correspond +to the field names in `where-expression' are used to fill in the +placeholders. Fetches the result set of the query into a list of +`record' objects.")) + +(defgeneric get-record (record-pkg record) + (:documentation "Returns a single `record' object. The instance of +`record' passed into this method is used to hold input parameters, +including the table name. Any slots that are populated with a non-nil +value will be used in the AND-separated WHERE clause. Meant for +queries that return only one unique row. Returns `nil' if no rows +were found matching the query. Warning: if more than one row is +returned by the query, this method will return the first one. Note +that you cannot control which row will be first because of the lack of +an ORDER BY clause. Use this only for queries that are supposed to +return no more than one row.")) + +(defgeneric get-record-where (record-pkg record where-expression) + (:documentation "Returns a single `record' object. The instance of +`record' passed into this method is used to hold input parameters, +including the table name. `where-expression' is a FORMAT-style string +which uses ~a for value placeholders. The slots of `record' that +correspond to the field names in `where-expression' are used to fill +in the placeholders. Meant for queries that return only one unique +row. Returns `nil' if no rows were found matching the query. +Warning: if more than one row is returned by the query, this method +will return the first one. Note that you cannot control which row +will be first because of the lack of an ORDER BY clause. Use this +only for queries that are supposed to return no more than one row.")) + +(defgeneric call-pg-function (record-pkg postgresql-record) + (:documentation "")) |
