branch: externals/sql-indent commit 47e9ccbd06b327320a47dd1da778e6797b7608ce Author: Alex Harsanyi <alexharsa...@gmail.com> Commit: Alex Harsanyi <alexharsa...@gmail.com>
Recognize left and right outer join statements #85 * sql-indent.el (sqlind-find-join-start): new defun to look for join statements, can find "left outer join" statements as well, see #85 (sqlind-syntax-in-select, sqlind-refine-syntax): use `sqlind-find-join-start` (sqlind-lineup-joins-to-anchor): only look for the first join keyword, to detect the case where they un on multiple lines --- sql-indent-test.el | 5 +++++ sql-indent.el | 56 ++++++++++++++++++++++++++++++++----------------- test-data/pr85-syn.eld | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++ test-data/pr85.sql | 33 +++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 19 deletions(-) diff --git a/sql-indent-test.el b/sql-indent-test.el index f8181d4..f9a5910 100644 --- a/sql-indent-test.el +++ b/sql-indent-test.el @@ -417,4 +417,9 @@ information read from DATA-FILE (as generated by "test-data/pr84.sql" "test-data/pr84-syn.eld")) +(ert-deftest sqlind-ert-pr85 () + (sqlind-ert-check-file-syntax + "test-data/pr85.sql" + "test-data/pr85-syn.eld")) + ;;; sql-indent-test.el ends here diff --git a/sql-indent.el b/sql-indent.el index 112b05a..8511150 100644 --- a/sql-indent.el +++ b/sql-indent.el @@ -1022,6 +1022,32 @@ reverse order (a stack) and is used to skip over nested blocks." (defconst sqlind-join-condition-regexp (regexp-opt '("on" "using" "and" "or") 'symbols)) +(defun sqlind-find-join-start (start limit) + "Look backwards for the start of a JOIN statement, +begin looking for it at the START position in the buffer, and +look backwards until LIMIT is reached. Returns the buffer +position where the JOIN condition starts, or nil if no JOIN +statement is found." + (save-excursion + (when (sqlind-search-backward start "\\bjoin\\b" limit) + (let ((candidate (point))) + (forward-char -1) + (sqlind-backward-syntactic-ws) + (backward-word) + (if (looking-at sqlind-select-join-regexp) + (point) + ;; The "outer" keyword is composed with a "left" or "right" keyword + ;; so we need to move backwards one more word. + (if (looking-at "\\bouter\\b") + (progn + (forward-char -1) + (sqlind-backward-syntactic-ws) + (backward-word) + (if (looking-at "\\b\\(left\\|right\\)\\b") + (point) + candidate)) + candidate)))))) + (defun sqlind-syntax-in-select (pos start) "Return the syntax ar POS which is inside a \"select\" statement at START." (save-excursion @@ -1069,15 +1095,9 @@ reverse order (a stack) and is used to skip over nested blocks." (when (or (looking-at sqlind-join-condition-regexp) (progn (forward-word -1) (looking-at sqlind-select-join-regexp))) ;; look for the join start, that will be the anchor - (when (sqlind-search-backward (point) "\\bjoin\\b" start) - (let ((candidate (point))) - (forward-char -1) - (sqlind-backward-syntactic-ws) - (backward-word) - (throw 'finished - (if (looking-at sqlind-select-join-regexp) - (cons 'select-join-condition (point)) - (cons 'select-join-condition candidate))))))) + (let ((jstart (sqlind-find-join-start (point) start))) + (when jstart + (throw 'finished (cons 'select-join-condition jstart)))))) ;; if this line starts with a ',' or the previous line starts ;; with a ',', we have a new table @@ -1478,15 +1498,9 @@ not a statement-continuation POS is the same as the (when (or (looking-at sqlind-join-condition-regexp) (progn (forward-word -1) (looking-at sqlind-join-condition-regexp))) ;; look for the join start, that will be the anchor - (when (sqlind-search-backward (point) "\\bjoin\\b" anchor) - (let ((candidate (point))) - (forward-char -1) - (sqlind-backward-syntactic-ws) - (backward-word) - (push (if (looking-at sqlind-select-join-regexp) - (cons 'select-join-condition (point)) - (cons 'select-join-condition candidate)) - context)))))) + (let ((jstart (sqlind-find-join-start (point) anchor))) + (when jstart + (push (cons 'select-join-condition jstart) context)))))) )) @@ -2236,7 +2250,11 @@ it will indent lines starting with JOIN keywords to line up with the FROM keyword." (save-excursion (back-to-indentation) - (if (looking-at (concat "\\b\\(" sqlind-select-join-regexp "\\s-+\\)?join\\b")) + ;; NOTE: we are a bit loose here as we only look for the first keyword + ;; which might indicate a join regexp, e.g. we are happy to see "left" + ;; even though, the correct statement is "left outer? join" + (if (or (looking-at sqlind-select-join-regexp) + (looking-at "\\bjoin\\b")) (sqlind-lineup-to-anchor syntax base-indentation) base-indentation))) diff --git a/test-data/pr85-syn.eld b/test-data/pr85-syn.eld new file mode 100644 index 0000000..7710e7d --- /dev/null +++ b/test-data/pr85-syn.eld @@ -0,0 +1,57 @@ +(((toplevel . 1)) + ((select-clause . 1) + (statement-continuation . 1)) + ((select-table-continuation . 12) + (statement-continuation . 1)) + ((select-join-condition . 30) + (statement-continuation . 1)) + ((toplevel . 1)) + ((toplevel . 1)) + ((select-clause . 82) + (statement-continuation . 82)) + ((select-table-continuation . 93) + (statement-continuation . 82)) + ((select-table-continuation . 93) + (statement-continuation . 82)) + ((select-join-condition . 111) + (statement-continuation . 82)) + ((toplevel . 1)) + ((toplevel . 1)) + ((select-clause . 172) + (statement-continuation . 172)) + ((select-table-continuation . 183) + (statement-continuation . 172)) + ((select-table-continuation . 183) + (statement-continuation . 172)) + ((select-join-condition . 201) + (statement-continuation . 172)) + ((toplevel . 1)) + ((toplevel . 1)) + ((select-clause . 290) + (statement-continuation . 290)) + ((select-table-continuation . 301) + (statement-continuation . 290)) + ((select-join-condition . 319) + (statement-continuation . 290)) + ((toplevel . 1)) + ((toplevel . 1)) + ((select-clause . 372) + (statement-continuation . 372)) + ((select-table-continuation . 383) + (statement-continuation . 372)) + ((select-table-continuation . 383) + (statement-continuation . 372)) + ((select-join-condition . 401) + (statement-continuation . 372)) + ((toplevel . 1)) + ((toplevel . 1)) + ((select-clause . 496) + (statement-continuation . 496)) + ((select-table-continuation . 507) + (statement-continuation . 496)) + ((select-table-continuation . 507) + (statement-continuation . 496)) + ((select-join-condition . 525) + (statement-continuation . 496)) + ((toplevel . 1))) + \ No newline at end of file diff --git a/test-data/pr85.sql b/test-data/pr85.sql new file mode 100644 index 0000000..7dd8aa7 --- /dev/null +++ b/test-data/pr85.sql @@ -0,0 +1,33 @@ +select * + from foo + left outer join bar + on foo.k = bar.k; + +select * + from foo + left + outer join bar + on foo.k = bar.k; + +select * + from foo + left outer -- test + join bar + on foo.k = bar.k; + +select * + from foo + right outer join bar + on foo.k = bar.k; + +select * + from foo + right -- test + outer join bar + on foo.k = bar.k; + +select * + from foo + right outer + join bar + on foo.k = bar.k;