diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d1da3e..5e42e90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# Changelog 1.33.11 +## Changes to S-SQL +- Added :analyze operator +- Added :using option for :delete-from +- Additional S-SQL Documentation +## Updated version numbers in the asd files # Changelog 1.33.10 ## Changes to S-SQL - Added :call operator to s-sql (so that you can use the s-sql syntax to call a Postgresql procedure) and substantial more s-sql examples in the documentation. diff --git a/README.md b/README.md index 3583e5f..1da19cb 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A Common Lisp PostgreSQL programming interface --- -Version 1.33.10 +Version 1.33.11 Postmodern is a Common Lisp library for interacting with [PostgreSQL](http://www.postgresql.org) databases. It is under active development. Features are: diff --git a/cl-postgres.asd b/cl-postgres.asd index d55d98f..0c1d810 100644 --- a/cl-postgres.asd +++ b/cl-postgres.asd @@ -16,7 +16,7 @@ :author "Marijn Haverbeke " :maintainer "Sabra Crolleton " :license "zlib" - :version "1.33.8" + :version "1.33.11" :depends-on ("md5" "split-sequence" "ironclad" "cl-base64" "uax-15" (:feature (:or :allegro :ccl :clisp :genera :armedbear :cmucl :lispworks :ecl) diff --git a/doc/index.html b/doc/index.html index ce47627..c21be01 100644 --- a/doc/index.html +++ b/doc/index.html @@ -1,7 +1,7 @@ - + Postmodern @@ -243,7 +243,7 @@

Table of Contents

-Version 1.33.10 +Version 1.33.11

diff --git a/doc/index.org b/doc/index.org index 8e27fdf..d034955 100644 --- a/doc/index.org +++ b/doc/index.org @@ -4,7 +4,7 @@ #+HTML_HEAD: #+OPTIONS: ^:nil -Version 1.33.10 +Version 1.33.11 Postmodern is a Common Lisp library for interacting with [[https://postgresql.org][PostgreSQL databases]]. Features are: diff --git a/doc/isolation-notes.html b/doc/isolation-notes.html index e9742ff..0df0baf 100644 --- a/doc/isolation-notes.html +++ b/doc/isolation-notes.html @@ -1,13 +1,14 @@ - - - + + + Transaction and Isolation Notes - - - + -

+

Transaction and Isolation Notes

-
-
-

S-SQL Examples Home Page

-
+
+

S-SQL Examples Home Page

+
@@ -304,29 +305,6 @@

S-SQL Examples Home Page -
-

And

-
-
-
    (query (:select 'countries.name
-                  :from 'countries 'regions
-                  :where (:and (:= 'regions.name "North America")
-                               (:= 'regions.id 'countries.region-id))))
-
-  (("Bermuda") ("Canada") ("Greenland") ("Mexico") ("US"))
-
-  (query (:select 'countries.name
-                  :from 'countries 'regions
-                  :where (:and (:= 'region-id 'regions.id)
-                               (:= 'regions.name "Central America")
-                               (:< 'latitude 12))))
-
-(("Costa Rica") ("Panama"))
-
-
-
-
-

Alter Table

@@ -385,6 +363,68 @@

Altering Columns

+
+

And

+
+
+
    (query (:select 'countries.name
+                  :from 'countries 'regions
+                  :where (:and (:= 'regions.name "North America")
+                               (:= 'regions.id 'countries.region-id))))
+
+  (("Bermuda") ("Canada") ("Greenland") ("Mexico") ("US"))
+
+  (query (:select 'countries.name
+                  :from 'countries 'regions
+                  :where (:and (:= 'region-id 'regions.id)
+                               (:= 'regions.name "Central America")
+                               (:< 'latitude 12))))
+
+(("Costa Rica") ("Panama"))
+
+
+
+
+ +
+

Analyze

+
+

+The official Postgresql documents note that analyze collects statistics about a database and provides various options, whether you want specific tables and columns or the entire database, etc. The following is a series of examples of how it can be used in s-sql with increasing levels of complexity. Options can be set to true with t, 1 or :on and set to false with :nil 0 or off. +

+
+
(query (:analyze))
+
+(query (:analyze :verbose))
+;; Now specifying just analyzing table t1
+(query (:analyze :verbose :tables 't1))
+
+(query (:analyze :verbose :tables :t1))
+;; Now specifying just analyzing tables t1 and t2
+(query (:analyze :verbose :tables :t1 :t2))
+
+(query (:analyze :verbose :tables 't1 't2))
+;; Now specifying columns in tables t1 and t2
+(query (:analyze :verbose
+        :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c2 't2c3)))
+;; Alternative syntax for the columns
+(query (:analyze :verbose :tables (:t1 :t1c1 :t1c2)))
+;; Starting to look at more specific options
+(query (:analyze :option (:verbose t)
+                 :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c)))
+;; The following will set option verbose to true, skip-locked to false
+;; and buffer-usage-limit to 70MB
+(query (:analyze :option (:verbose 1) (:skip-locked 0) (:buffer-usage-limit "70MB")
+                 :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c)))
+;; The following will set option verbose to true, skip-locked to false
+;; and buffer-usage-limit to 70MB
+(query (:analyze :option (:verbose t) (:skip-locked nil) (:buffer-usage-limit "70MB")
+                 :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c)))
+
+
+
+
+

Any, Any*

@@ -463,7 +503,8 @@

Any, Any*

:where (:= 'id (:any '$1))) toy-query)) -;; Evaluation aborted on #<CL-POSTGRES-ERROR:SYNTAX-ERROR-OR-ACCESS-VIOLATION {10030AF6A1}>. +;; Evaluation aborted on +;; #<CL-POSTGRES-ERROR:SYNTAX-ERROR-OR-ACCESS-VIOLATION {10030AF6A1}>. (let ((toy-query '(21 22))) (query (:select 'name diff --git a/doc/s-sql-a.org b/doc/s-sql-a.org index dd5d5d6..8b1a397 100644 --- a/doc/s-sql-a.org +++ b/doc/s-sql-a.org @@ -7,27 +7,6 @@ * [[file:s-sql-examples.org][S-SQL Examples Home Page]] | [[file:s-sql-a.org][A]]| [[file:s-sql-b.org][B]]| [[file:s-sql-c.org][C]]| [[file:s-sql-d.org][D]]| [[file:s-sql-e.org][E]]| [[file:s-sql-f.org][F]]| [[file:s-sql-g.org][G]]| [[file:s-sql-h.org][H]]| [[file:s-sql-i.org][I]]| [[file:s-sql-j.org][J]]| [[file:s-sql-k.org][K]]| [[file:s-sql-l.org][L]]| [[file:s-sql-m.org][M]]| [[file:s-sql-n.org][N]]| [[file:s-sql-o.org][O]]| [[file:s-sql-p.org][P]]| [[file:s-sql-r.org][R]]| [[file:s-sql-s.org][S]]| [[file:s-sql-t.org][T]]| [[file:s-sql-u.org][U]]| [[file:s-sql-v.org][V]]| [[file:s-sql-w.org][W]]| [[file:s-sql-special-characters.org][Special Characters]] | [[file:calling-postgresql-stored-functions.org][Calling Postgresql Stored Functions and Procedures]]| -* And - :PROPERTIES: - :CUSTOM_ID: and - :END: -#+begin_src lisp - (query (:select 'countries.name - :from 'countries 'regions - :where (:and (:= 'regions.name "North America") - (:= 'regions.id 'countries.region-id)))) - - (("Bermuda") ("Canada") ("Greenland") ("Mexico") ("US")) - - (query (:select 'countries.name - :from 'countries 'regions - :where (:and (:= 'region-id 'regions.id) - (:= 'regions.name "Central America") - (:< 'latitude 12)))) - - (("Costa Rica") ("Panama")) -#+end_src - * Alter Table :PROPERTIES: :CUSTOM_ID: alter-table @@ -69,6 +48,62 @@ and you want to drop the not null constraint on the table. Either of the followi #+end_src +* And + :PROPERTIES: + :CUSTOM_ID: and + :END: +#+begin_src lisp + (query (:select 'countries.name + :from 'countries 'regions + :where (:and (:= 'regions.name "North America") + (:= 'regions.id 'countries.region-id)))) + + (("Bermuda") ("Canada") ("Greenland") ("Mexico") ("US")) + + (query (:select 'countries.name + :from 'countries 'regions + :where (:and (:= 'region-id 'regions.id) + (:= 'regions.name "Central America") + (:< 'latitude 12)))) + + (("Costa Rica") ("Panama")) +#+end_src + +* Analyze + :PROPERTIES: + :CUSTOM_ID: analyze + :END: + The [[https://www.postgresql.org/docs/current/sql-analyze.html][official Postgresql documents]] note that analyze collects statistics about a database and provides various options, whether you want specific tables and columns or the entire database, etc. The following is a series of examples of how it can be used in s-sql with increasing levels of complexity. Options can be set to true with t, 1 or :on and set to false with :nil 0 or off. +#+begin_src lisp + (query (:analyze)) + + (query (:analyze :verbose)) + ;; Now specifying just analyzing table t1 + (query (:analyze :verbose :tables 't1)) + + (query (:analyze :verbose :tables :t1)) + ;; Now specifying just analyzing tables t1 and t2 + (query (:analyze :verbose :tables :t1 :t2)) + + (query (:analyze :verbose :tables 't1 't2)) + ;; Now specifying columns in tables t1 and t2 + (query (:analyze :verbose + :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c2 't2c3))) + ;; Alternative syntax for the columns + (query (:analyze :verbose :tables (:t1 :t1c1 :t1c2))) + ;; Starting to look at more specific options + (query (:analyze :option (:verbose t) + :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c))) + ;; The following will set option verbose to true, skip-locked to false + ;; and buffer-usage-limit to 70MB + (query (:analyze :option (:verbose 1) (:skip-locked 0) (:buffer-usage-limit "70MB") + :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c))) + ;; The following will set option verbose to true, skip-locked to false + ;; and buffer-usage-limit to 70MB + (query (:analyze :option (:verbose t) (:skip-locked nil) (:buffer-usage-limit "70MB") + :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c))) + +#+end_src * Any, Any* :PROPERTIES: :CUSTOM_ID: any @@ -131,7 +166,8 @@ Now using s-sql and keeping with the toy example, notice that using :any does no :where (:= 'id (:any '$1))) toy-query)) - ;; Evaluation aborted on #. + ;; Evaluation aborted on + ;; #. (let ((toy-query '(21 22))) (query (:select 'name diff --git a/doc/s-sql-d.html b/doc/s-sql-d.html index 8182c06..6665e27 100644 --- a/doc/s-sql-d.html +++ b/doc/s-sql-d.html @@ -1,7 +1,7 @@ - + S-SQL Examples D @@ -202,7 +202,7 @@

S-SQL Examples D

Table of Contents

-

@@ -424,8 +424,10 @@

Default with no timezone or offset:

Using offset to explicitly offset 1 hour from UTC (e.g. Paris)

-
(query (:insert-into 'test :set 'id 4 'text "offset 1 hour"
-                         'date (local-time:encode-timestamp 0 0 0 12 01 01 2013 :offset 3600)))
+
(query (:insert-into 'test
+        :set 'id 4 'text "offset 1 hour"
+                     'date (local-time:encode-timestamp 0 0 0 12 01 01 2013
+                                                        :offset 3600)))
 
 2013-01-01 03:00:00-08
 
@@ -439,9 +441,10 @@

Default with no timezone or offset:

Using timezone to explicitly set it for UTC

-
  (query (:insert-into 'test
-                           :set 'id 5 'text "insert here using timezone utc"
-                           'date (local-time:encode-timestamp 0 0 0 12 01 01 2013 :timezone local-time::+utc-zone+)))
+
(query (:insert-into 'test
+        :set 'id 5 'text "insert here using timezone utc"
+                     'date (local-time:encode-timestamp 0 0 0 12 01 01 2013
+                                                        :timezone local-time::+utc-zone+)))
 
 2013-01-01 04:00:00-08
 
@@ -490,6 +493,22 @@

Delete

:where (:= 'memid 'mems.memid))))))
+

+The following example shows the application of the :using option: +

+
+
(query (:delete-from 'members
+        :using 'producers
+        :where (:and (:= 'members.id 'producers.id)
+                     (:= 'members.name "Steve"))))
+
+(sql (:delete-from 'members
+      :using 'producers 'films
+                   :where (:and (:= 'members.id 'producers.id)
+                                (:= 'members.name "Steve")
+                                (:= 'producers.films-id 'films.id)))
+
+
@@ -500,14 +519,14 @@

Desc

Normally, the use of :order-by would order the results in ascending order. You can apply :desc to a column and :asc to another column to re-arrange how the order-by rules will work.

-
(query (:order-by
-        (:select 'id 'name :from 'regions)
-        (:desc 'id)))
-
-((11 "Eastern Europe") (10 "Caribbean") (9 "Pacific") (8 "Central Asia")
-                       (7 "South America") (6 "North America") (5 "Middle East")
-                       (4 "Western Europe") (3 "Central America") (2 "Asia")
-                       (1 "Africa"))
+
    (query (:order-by
+            (:select 'id 'name :from 'regions)
+            (:desc 'id)))
+
+((11 "Eastern Europe") (10 "Caribbean") (9 "Pacific")
+ (8 "Central Asia")(7 "South America") (6 "North America")
+ (5 "Middle East")(4 "Western Europe") (3 "Central America")
+ (2 "Asia") (1 "Africa"))
 
@@ -549,9 +568,10 @@

Distinct On

(query (:select 'id 'name 'region-id :distinct-on 'region-id
         :from 'countries))
 
-((165 "Gabon" 1) (102 "Nepal" 2) (73 "Nicaragua" 3) (20 "UK" 4) (51 "Egypt" 5)
-                 (166 "Greenland" 6) (75 "Honduras" 7) (184 "Turkmenistan" 8)(108 "Papua New Guinea" 9)
-                 (121 "Antigua" 10) (67 "Belarus" 11))
+((165 "Gabon" 1) (102 "Nepal" 2) (73 "Nicaragua" 3) (20 "UK" 4)
+ (51 "Egypt" 5) (166 "Greenland" 6) (75 "Honduras" 7)
+ (184 "Turkmenistan" 8)(108 "Papua New Guinea" 9)
+ (121 "Antigua" 10) (67 "Belarus" 11))
 
 (query (:order-by
         (:select 'location 'time 'report
diff --git a/doc/s-sql-d.org b/doc/s-sql-d.org
index 9ab3d45..3e57d41 100644
--- a/doc/s-sql-d.org
+++ b/doc/s-sql-d.org
@@ -87,8 +87,10 @@ So, looking at a server that is set for PDT, for table test with fields id, date
 
 Using offset to explicitly offset 1 hour from UTC (e.g. Paris)
 #+begin_src lisp
-  (query (:insert-into 'test :set 'id 4 'text "offset 1 hour"
-                           'date (local-time:encode-timestamp 0 0 0 12 01 01 2013 :offset 3600)))
+  (query (:insert-into 'test
+          :set 'id 4 'text "offset 1 hour"
+                       'date (local-time:encode-timestamp 0 0 0 12 01 01 2013
+                                                          :offset 3600)))
 
   2013-01-01 03:00:00-08
 #+end_src
@@ -98,10 +100,11 @@ Using offset to explicitly offset 1 hour from UTC (e.g. Paris)
 Using timezone to explicitly set it for UTC
 #+begin_src lisp
   (query (:insert-into 'test
-                           :set 'id 5 'text "insert here using timezone utc"
-                           'date (local-time:encode-timestamp 0 0 0 12 01 01 2013 :timezone local-time::+utc-zone+)))
+          :set 'id 5 'text "insert here using timezone utc"
+                       'date (local-time:encode-timestamp 0 0 0 12 01 01 2013
+                                                          :timezone local-time::+utc-zone+)))
 
-2013-01-01 04:00:00-08
+  2013-01-01 04:00:00-08
 #+end_src
  (looking at the default timezone for the server, postgresql has kept the timezone as PDT - UTC less 8 hours - but set the time as 04:00:00, which is the time in UTC relative to the PDT time at the server.
 
@@ -132,6 +135,19 @@ Slightly more complicated versions:
                                  :from 'cd.bookings
                                  :where (:= 'memid 'mems.memid))))))
 #+end_src
+The following example shows the application of the :using option:
+#+begin_src lisp
+  (query (:delete-from 'members
+          :using 'producers
+          :where (:and (:= 'members.id 'producers.id)
+                       (:= 'members.name "Steve"))))
+
+  (sql (:delete-from 'members
+        :using 'producers 'films
+                     :where (:and (:= 'members.id 'producers.id)
+                                  (:= 'members.name "Steve")
+                                  (:= 'producers.films-id 'films.id)))
+#+end_src
 
 * Desc
   :PROPERTIES:
@@ -143,10 +159,10 @@ Normally, the use of :order-by would order the results in ascending order. You c
               (:select 'id 'name :from 'regions)
               (:desc 'id)))
 
-      ((11 "Eastern Europe") (10 "Caribbean") (9 "Pacific") (8 "Central Asia")
-                             (7 "South America") (6 "North America") (5 "Middle East")
-                             (4 "Western Europe") (3 "Central America") (2 "Asia")
-                             (1 "Africa"))
+  ((11 "Eastern Europe") (10 "Caribbean") (9 "Pacific")
+   (8 "Central Asia")(7 "South America") (6 "North America")
+   (5 "Middle East")(4 "Western Europe") (3 "Central America")
+   (2 "Asia") (1 "Africa"))
 #+end_src
 
 * Distinct
@@ -176,9 +192,10 @@ The postmodern s-sql syntax would look like:
   (query (:select 'id 'name 'region-id :distinct-on 'region-id
           :from 'countries))
 
-  ((165 "Gabon" 1) (102 "Nepal" 2) (73 "Nicaragua" 3) (20 "UK" 4) (51 "Egypt" 5)
-                   (166 "Greenland" 6) (75 "Honduras" 7) (184 "Turkmenistan" 8)(108 "Papua New Guinea" 9)
-                   (121 "Antigua" 10) (67 "Belarus" 11))
+  ((165 "Gabon" 1) (102 "Nepal" 2) (73 "Nicaragua" 3) (20 "UK" 4)
+   (51 "Egypt" 5) (166 "Greenland" 6) (75 "Honduras" 7)
+   (184 "Turkmenistan" 8)(108 "Papua New Guinea" 9)
+   (121 "Antigua" 10) (67 "Belarus" 11))
 
   (query (:order-by
           (:select 'location 'time 'report
diff --git a/doc/s-sql-examples.org b/doc/s-sql-examples.org
index 4149c79..15fb67b 100644
--- a/doc/s-sql-examples.org
+++ b/doc/s-sql-examples.org
@@ -11,7 +11,7 @@
 | [[file:s-sql-a.org][A]]| [[file:s-sql-b.org][B]]| [[file:s-sql-c.org][C]]| [[file:s-sql-d.org][D]]| [[file:s-sql-e.org][E]]| [[file:s-sql-f.org][F]]| [[file:s-sql-g.org][G]]| [[file:s-sql-h.org][H]]| [[file:s-sql-i.org][I]]| [[file:s-sql-j.org][J]]| [[file:s-sql-k.org][K]]| [[file:s-sql-l.org][L]]| [[file:s-sql-m.org][M]]| [[file:s-sql-n.org][N]]| [[file:s-sql-o.org][O]]| [[file:s-sql-p.org][P]]| [[file:s-sql-r.org][R]]| [[file:s-sql-s.org][S]]| [[file:s-sql-t.org][T]]| [[file:s-sql-u.org][U]]| [[file:s-sql-v.org][V]]| [[file:s-sql-w.org][W]]|  [[file:s-sql-special-characters.org][Special Characters]]                        |  [[file:calling-postgresql-stored-functions.org][Calling Postgresql Stored Functions and Procedures]]|
 
 - [[file:intro-to-s-sql.org][Intro to S-SQL]]
-- [[file:s-sql-a.org][a (and, alter-table, any and any*, as or alias, avg)]]
+- [[file:s-sql-a.org][a (alter-table, and, analyze, any and any*, as or alias, avg)]]
 - [[file:s-sql-b.org][b (Between, Boolean Operators)]]
 - [[file:s-sql-c.org][c (call, case, cast, coalesce, constraints, count, create-composite-types create-index, create-table)]]
 - [[file:s-sql-d.org][d (data types (numbers and timestamps), delete, desc, distinct, distinct-on, doquery)]]
diff --git a/doc/s-sql.html b/doc/s-sql.html
index e3c89a4..90b89c5 100644
--- a/doc/s-sql.html
+++ b/doc/s-sql.html
@@ -1,7 +1,7 @@
 
 
 
-
+
 
 
 S-SQL Reference Manual
@@ -362,14 +362,14 @@ 

Table of Contents

  • sql-op :variance (&rest args)
  • sql-op :var-pop (&rest args)
  • sql-op :var-samp (&rest args)
  • -
  • sql-op :range-between (&rest args)
  • -
  • sql-op :rows-between (&rest args)
  • +
  • sql-op :range-between (&rest args)
  • +
  • sql-op :rows-between (&rest args)
  • sql-op :over (form &rest args)
  • sql-op :partition-by (&rest args)
  • sql-op :window (form)
  • sql-op :with (&rest args)
  • sql-op :with-recursive (&rest args)
  • -
  • sql-op :with-ordinality, :with-ordinality-as
  • +
  • sql-op :with-ordinality, :with-ordinality-as
  • Table Functions @@ -407,6 +407,7 @@

    Table of Contents

  • sql-op :copy (table &rest args)
  • +
  • Analyze
  • Dynamic Queries, Composition and Parameterized Queries
    • Overview @@ -2828,9 +2829,9 @@

      sql-op :var-samp (&rest args)

      -
      -

      sql-op :range-between (&rest args)

      -
      +
      +

      sql-op :range-between (&rest args)

      +

      Range-between allows window functions to apply to different segments of a result set. It accepts the following keywords: :order-by, :rows-between, :range-between, @@ -2858,9 +2859,9 @@

      sql-op :range-between (&rest args)

      -
      -

      sql-op :rows-between (&rest args)

      -
      +
      +

      sql-op :rows-between (&rest args)

      +

      Rows-between allows window functions to apply to different segments of a result set. It accepts the following keywords: @@ -3061,9 +3062,9 @@

      sql-op :with-recursive (&rest args)

      -
      -

      sql-op :with-ordinality, :with-ordinality-as

      -
      +
      +

      sql-op :with-ordinality, :with-ordinality-as

      +

      Selects can use :with-ordinality or :with-ordinality-as parameters. Postgresql will give the new ordinality column the name of ordinality. :with-ordinality-as allows you to set different names for the columns in the result set.

      @@ -3399,6 +3400,22 @@

      sql-op :delete-from (table &rest rest)

      (query (:delete-from 'cd.bookings :where (:= 'id 5)))
       
      +

      +The following more complicated version shows the application of the :using and :where clauses: +

      +
      +
      (query (:delete-from 'members
      +        :using 'producers
      +        :where (:and (:= 'members.id 'producers.id)
      +                     (:= 'members.name "Steve"))))
      +
      +(sql (:delete-from 'members
      +      :using 'producers 'films
      +                   :where (:and (:= 'members.id 'producers.id)
      +                                (:= 'members.name "Steve")
      +                                (:= 'producers.films-id 'films.id)))
      +
      +
      @@ -4091,6 +4108,45 @@

      sql-op :copy (table &rest args)

      +
      +

      Analyze

      +
      +

      +The official Postgresql documents note that analyze collects statistics about a database and provides various options, whether you want specific tables and columns or the entire database, etc. The following is a series of examples of how it can be used in s-sql with increasing levels of complexity. Options can be set to true with t, 1 or :on and set to false with :nil 0 or off. +

      +
      +
      (query (:analyze))
      +
      +(query (:analyze :verbose))
      +;; Now specifying just analyzing table t1
      +(query (:analyze :verbose :tables 't1))
      +
      +(query (:analyze :verbose :tables :t1))
      +;; Now specifying just analyzing tables t1 and t2
      +(query (:analyze :verbose :tables :t1 :t2))
      +
      +(query (:analyze :verbose :tables 't1 't2))
      +;; Now specifying columns in tables t1 and t2
      +(query (:analyze :verbose
      +        :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c2 't2c3)))
      +;; Alternative syntax for the columns
      +(query (:analyze :verbose :tables (:t1 :t1c1 :t1c2)))
      +;; Starting to look at more specific options
      +(query (:analyze :option (:verbose t)
      +                 :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c)))
      +;; The following will set option verbose to true, skip-locked to false
      +;; and buffer-usage-limit to 70MB
      +(query (:analyze :option (:verbose 1) (:skip-locked 0) (:buffer-usage-limit "70MB")
      +                 :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c)))
      +;; The following will set option verbose to true, skip-locked to false
      +;; and buffer-usage-limit to 70MB
      +(query (:analyze :option (:verbose t) (:skip-locked nil) (:buffer-usage-limit "70MB")
      +                 :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c)))
      +
      +
      +
      +
      +

      Dynamic Queries, Composition and Parameterized Queries

      @@ -4177,8 +4233,8 @@

      Programmer Built Queries

        -
      • Select Statements
        -
        +
      • Select Statements
        +

        Consider the following two toy examples where we determine the table and columns to be selected using symbols (either keyword or quoted) inside variables. @@ -4202,8 +4258,8 @@

        Programmer Built Queries

      • -
      • Update Statements
        -
        +
      • Update Statements
        +

        This works with update statements as well

        @@ -4216,8 +4272,8 @@

        Programmer Built Queries

    • -
    • Insert Statements
      -
      +
    • Insert Statements
      +

      This works with insert-into statements as well

      @@ -4238,8 +4294,8 @@

      Programmer Built Queries

  • -
  • Delete Statements
    -
    +
  • Delete Statements
    +

    This works with delete statements as well

    diff --git a/doc/s-sql.org b/doc/s-sql.org index 08eb71b..a39212a 100644 --- a/doc/s-sql.org +++ b/doc/s-sql.org @@ -2301,6 +2301,19 @@ expressions that should be returned for every deleted row. #+BEGIN_SRC lisp (query (:delete-from 'cd.bookings :where (:= 'id 5))) #+END_SRC +The following more complicated version shows the application of the :using and :where clauses: +#+begin_src lisp + (query (:delete-from 'members + :using 'producers + :where (:and (:= 'members.id 'producers.id) + (:= 'members.name "Steve")))) + + (sql (:delete-from 'members + :using 'producers 'films + :where (:and (:= 'members.id 'producers.id) + (:= 'members.name "Steve") + (:= 'producers.films-id 'films.id))) +#+end_src ** sql-op :create-table (name (&rest columns) &rest options) :PROPERTIES: :CUSTOM_ID: sql-op-create-table @@ -2793,6 +2806,41 @@ tutorial: :segment-reject-limit 100 'ROWS)) #+END_SRC +* Analyze + :PROPERTIES: + :CUSTOM_ID: analyze + :END: + The [[https://www.postgresql.org/docs/current/sql-analyze.html][official Postgresql documents]] note that analyze collects statistics about a database and provides various options, whether you want specific tables and columns or the entire database, etc. The following is a series of examples of how it can be used in s-sql with increasing levels of complexity. Options can be set to true with t, 1 or :on and set to false with :nil 0 or off. +#+begin_src lisp + (query (:analyze)) + + (query (:analyze :verbose)) + ;; Now specifying just analyzing table t1 + (query (:analyze :verbose :tables 't1)) + + (query (:analyze :verbose :tables :t1)) + ;; Now specifying just analyzing tables t1 and t2 + (query (:analyze :verbose :tables :t1 :t2)) + + (query (:analyze :verbose :tables 't1 't2)) + ;; Now specifying columns in tables t1 and t2 + (query (:analyze :verbose + :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c2 't2c3))) + ;; Alternative syntax for the columns + (query (:analyze :verbose :tables (:t1 :t1c1 :t1c2))) + ;; Starting to look at more specific options + (query (:analyze :option (:verbose t) + :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c))) + ;; The following will set option verbose to true, skip-locked to false + ;; and buffer-usage-limit to 70MB + (query (:analyze :option (:verbose 1) (:skip-locked 0) (:buffer-usage-limit "70MB") + :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c))) + ;; The following will set option verbose to true, skip-locked to false + ;; and buffer-usage-limit to 70MB + (query (:analyze :option (:verbose t) (:skip-locked nil) (:buffer-usage-limit "70MB") + :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c))) + +#+end_src * Dynamic Queries, Composition and Parameterized Queries :PROPERTIES: :CUSTOM_ID: dynamic-queries-composition-and-parameterized-queries diff --git a/postmodern.asd b/postmodern.asd index 6870246..d42bc82 100644 --- a/postmodern.asd +++ b/postmodern.asd @@ -20,7 +20,7 @@ :maintainer "Sabra Crolleton " :homepage "https://github.com/marijnh/Postmodern" :license "zlib" - :version "1.33.8" + :version "1.33.11" :depends-on ("alexandria" "cl-postgres" "s-sql" diff --git a/s-sql.asd b/s-sql.asd index be74df0..3c1f2fa 100644 --- a/s-sql.asd +++ b/s-sql.asd @@ -9,7 +9,7 @@ :author "Marijn Haverbeke " :maintainer "Sabra Crolleton " :license "zlib" - :version "1.33.8" + :version "1.33.11" :depends-on ("cl-postgres" "alexandria") :components diff --git a/s-sql/tests/tests.lisp b/s-sql/tests/tests.lisp index 734e673..29e7d78 100644 --- a/s-sql/tests/tests.lisp +++ b/s-sql/tests/tests.lisp @@ -634,28 +634,38 @@ to strings \(which will form an SQL query when concatenated)." (is (equal (sql (:select 'ta :from 'a :where (:not-null 'ta))) "(SELECT ta FROM a WHERE (ta IS NOT NULL))"))) -(test analyse - (is (equal (sql (:analyze :option (:verbose t) :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c))) - "ANALYZE (VERBOSE TRUE) t1(t1c1, t1c2), t2(t2c1, t2c)")) - (is (equal (sql (:analyze :option (:verbose 1) (:skip-locked 0) (:buffer-usage-limit "70MB") :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c))) - "ANALYZE (VERBOSE TRUE, SKIP_LOCKED FALSE, BUFFER_USAGE_LIMIT 70MB) t1(t1c1, t1c2), t2(t2c1, t2c)")) - (is (equal (sql (:analyze :option (:verbose :off) (:skip-locked :on) (:buffer-usage-limit "70MB") :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c))) - "ANALYZE (VERBOSE FALSE, SKIP_LOCKED TRUE, BUFFER_USAGE_LIMIT 70MB) t1(t1c1, t1c2), t2(t2c1, t2c)")) - (is (equal (sql (:analyze :verbose :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c2 't2c3))) - "ANALYZE VERBOSE t1(t1c1, t1c2), t2(t2c1, t2c2, t2c3)")) - (is (equal (sql (:analyze :verbose)) - "ANALYZE VERBOSE ")) - (is (equal (sql (:analyze)) - "ANALYZE ")) - (is (equal (sql (:analyze :option (:verbose t) (:skip-locked nil) (:buffer-usage-limit "70MB") :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c))) - "ANALYZE (VERBOSE TRUE, SKIP_LOCKED FALSE, BUFFER_USAGE_LIMIT 70MB) t1(t1c1, t1c2), t2(t2c1, t2c)")) - (is (equal (sql (:analyze :option (:verbose t) (:skip-locked nil) (:buffer-usage-limit "70MB") :tables (:t1 't1c1 't1c2))) - "ANALYZE (VERBOSE TRUE, SKIP_LOCKED FALSE, BUFFER_USAGE_LIMIT 70MB) t1(t1c1, t1c2)")) - (is (equal (sql (:analyze :option (:verbose t) (:skip-locked nil) (:buffer-usage-limit "70MB") :tables (:t1 't1c1 't1c2))) - "ANALYZE (VERBOSE TRUE, SKIP_LOCKED FALSE, BUFFER_USAGE_LIMIT 70MB) t1(t1c1, t1c2)")) - (is (equal (sql (:analyze :verbose :option (:verbose t) (:skip-locked nil) (:buffer-usage-limit "70MB") :tables (:t1 't1c1 't1c2))) - "ANALYZE (VERBOSE TRUE, SKIP_LOCKED FALSE, BUFFER_USAGE_LIMIT 70MB) t1(t1c1, t1c2)"))) - +(test analyze + (is (equal (sql (:analyze :option (:verbose t) :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c))) + "ANALYZE (VERBOSE TRUE) t1(t1c1, t1c2), t2(t2c1, t2c)")) + (is (equal (sql (:analyze :option (:verbose 1) (:skip-locked 0) (:buffer-usage-limit "70MB") + :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c))) + "ANALYZE (VERBOSE TRUE, SKIP_LOCKED FALSE, BUFFER_USAGE_LIMIT 70MB) t1(t1c1, t1c2), t2(t2c1, t2c)")) + (is (equal (sql (:analyze :option (:verbose :off) (:skip-locked :on) (:buffer-usage-limit "70MB") + :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c))) + "ANALYZE (VERBOSE FALSE, SKIP_LOCKED TRUE, BUFFER_USAGE_LIMIT 70MB) t1(t1c1, t1c2), t2(t2c1, t2c)")) + (is (equal (sql (:analyze :verbose :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c2 't2c3))) + "ANALYZE VERBOSE t1(t1c1, t1c2), t2(t2c1, t2c2, t2c3)")) + (is (equal (sql (:analyze :verbose)) + "ANALYZE VERBOSE ")) + (is (equal (sql (:analyze)) + "ANALYZE ")) + (is (equal (sql (:analyze :verbose :tables 't1)) + "ANALYZE VERBOSE t1")) + (is (equal (sql (:analyze :verbose :tables :t1)) + "ANALYZE VERBOSE t1")) + (is (equal (sql (:analyze :option (:verbose t) (:skip-locked nil) (:buffer-usage-limit "70MB") + :tables (:t1 't1c1 't1c2) (:t2 't2c1 't2c))) + "ANALYZE (VERBOSE TRUE, SKIP_LOCKED FALSE, BUFFER_USAGE_LIMIT 70MB) t1(t1c1, t1c2), t2(t2c1, t2c)")) + (is (equal (sql (:analyze :option (:verbose t) (:skip-locked nil) (:buffer-usage-limit "70MB") + :tables (:t1 't1c1 't1c2))) + "ANALYZE (VERBOSE TRUE, SKIP_LOCKED FALSE, BUFFER_USAGE_LIMIT 70MB) t1(t1c1, t1c2)")) + (is (equal (sql (:analyze :option (:verbose t) (:skip-locked nil) (:buffer-usage-limit "70MB") + :tables (:t1 't1c1 't1c2))) + "ANALYZE (VERBOSE TRUE, SKIP_LOCKED FALSE, BUFFER_USAGE_LIMIT 70MB) t1(t1c1, t1c2)")) + (is (equal (sql (:analyze :verbose :option (:verbose t) (:skip-locked nil) (:buffer-usage-limit "70MB") + :tables (:t1 't1c1 't1c2))) + "ANALYZE (VERBOSE TRUE, SKIP_LOCKED FALSE, BUFFER_USAGE_LIMIT 70MB) t1(t1c1, t1c2)"))) + (test in-itself (is (equal (sql (:in 'id (:set (list 2 7 8)))) "(id IN (2, 7, 8))"))