+++ /dev/null
-;;; sql-indent-autoloads.el --- automatically extracted autoloads
-;;
-;;; Code:
-(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path))))
-\f
-;;;### (autoloads nil nil ("sql-indent.el") (23994 46299 102574 67000))
-
-;;;***
-\f
-;; Local Variables:
-;; version-control: never
-;; no-byte-compile: t
-;; no-update-autoloads: t
-;; End:
-;;; sql-indent-autoloads.el ends here
+++ /dev/null
-;;; -*- no-byte-compile: t -*-
-(define-package "sql-indent" "1.10" "indentation of SQL statements" 'nil)
+++ /dev/null
-;;; sql-indent.el --- indentation of SQL statements
-
-;; Copyright (C) 2000 Alex Schroeder
-
-;; Authors: Alex Schroeder <alexNOSPAMgnu.org>
-;; Matt Henry <mcthenry+gnuNOSPAMgmail.com>
-;; Maintainer: Matt Henry <mcthenry+gnuNOSPAMgmail.com>
-;; Version: 1.10
-;; Original-Version: $Id: sql-indent.el,v 1.10 2009/03/25 22:52:25 mhenry Exp $
-
-;; Keywords: languages
-;; URL: http://www.emacswiki.org/cgi-bin/wiki.pl?SqlIndent
-
-;; This file is not part of GNU Emacs.
-
-;; This is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
-
-;; This is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING. If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
-
-;;; Commentary:
-
-;; Indent SQL statements.
-
-;; As the indentation of SQL statements depends not only on the previous
-;; line but also on the current line, empty lines cannot always be
-;; indented correctly.
-
-;; Usage note: Loading this file will make all SQL mode buffers created
-;; from then on use `sql-indent-line' for indentation. A possible way
-;; to install sql-indent.el would be to add the following to your
-;; .emacs:
-
-;; (eval-after-load "sql"
-;; '(load-library "sql-indent"))
-
-;; Thanks:
-;; Arcady Genkin <antipodeNOSPAMthpoon.com>
-
-
-;;; History:
-;; 2009-03-22*
-;; * mhenry
-;; Added `sql-indent-buffer' for efficient full buffer processing.
-;; Modified `sql-indent' to be savvy to comments and strings.
-;; Removed "and", "or" and "exists" from `sql-indent-first-column-regexp'
-;; Added "create", "drop" and "truncate" to `sql-indent-first-column-regexp'
-
-;;; Code:
-
-(require 'sql)
-
-;; Need the following to allow GNU Emacs 19 to compile the file.
-(require 'regexp-opt)
-
-(defcustom sql-indent-first-column-regexp
- (concat "^\\s-*" (regexp-opt '(
- "select" "update" "insert" "delete"
- "union" "intersect"
- "from" "where" "into" "group" "having" "order"
- "set"
- "create" "drop" "truncate"
- "--") t) "\\(\\b\\|\\s-\\)")
- "Regexp matching keywords relevant for indentation.
-The regexp matches lines which start SQL statements and it matches lines
-that should be indented at the same column as the start of the SQL
-statement. The regexp is created at compile-time. Take a look at the
-source before changing it. All lines not matching this regexp will be
-indented by `sql-indent-offset'."
- :type 'regexp
- :group 'SQL)
-
-(defcustom sql-indent-offset 4
- "*Offset for SQL indentation."
- :type 'number
- :group 'SQL)
-
-(defcustom sql-indent-maybe-tab nil
- "If non-nil, call `insert-tab' if `current-column' did not change."
- :type 'boolean
- :group 'SQL)
-
-(defvar sql-indent-debug nil
- "If non-nil, `sql-indent-line' will output debugging messages.")
-
-(defun sql-indent-is-string-or-comment ()
- "Return nil if point is not in a comment or string; non-nil otherwise."
- (let ((parse-state (syntax-ppss)))
- (or (nth 3 parse-state) ; String
- (nth 4 parse-state))) ; Comment
- )
-
-(defun sql-indent-get-last-line-start ()
- "Find the last non-blank line. Return the beginning position of that line and its indentation."
-
- (save-excursion
- (forward-line -1)
-
- (while (and (not (bobp))
- (or
- (looking-at "^\\s-*$")
- (sql-indent-is-string-or-comment)) ; Skip comments or strings
- )
-
- (forward-line -1))
- (list (point) (current-indentation))
- )
- )
-
-(defun sql-indent-level-delta (&optional prev-start prev-indent)
- "Calculate the change in level from the previous non-blank line.
-Given the optional parameter `PREV-START' and `PREV-INDENT', assume that to be
-the previous non-blank line.
-Return a list containing the level change and the previous indentation."
-
- (save-excursion
- ;; Go back to the previous non-blank line
- (let* ((p-line (cond ((and prev-start prev-indent)
- (list prev-start prev-indent))
- ((sql-indent-get-last-line-start))))
- (curr-start (progn (beginning-of-line)
- (point)))
- (paren (nth 0 (parse-partial-sexp (nth 0 p-line) curr-start))))
-
- ;; Add opening or closing parens.
- ;; If the current line starts with a keyword statement (e.g. SELECT, FROM, ...) back up one level
- ;; If the previous line starts with a keyword statement then add one level
-
- (list
- (+ paren
- (if (progn (goto-char (nth 0 p-line))
- (looking-at sql-indent-first-column-regexp))
- 1
- 0)
- (if (progn (goto-char curr-start)
- (looking-at sql-indent-first-column-regexp))
- -1
- 0)
- )
- (nth 1 p-line))
- )
- )
- )
-
-(defun sql-indent-buffer ()
- "Indent the buffer's SQL statements."
- (interactive)
- (save-excursion
- (beginning-of-buffer)
- (let*
- ((line 0)
- (level 0)
- (start (point))
- (indent (if (looking-at "^\\s-*$")
- 0
- (current-indentation)))
- (this-indent 0)
- (vals '()))
-
- (while (/= (point) (point-max))
- (forward-line)
-
- (setq vals
- (sql-indent-level-delta start indent)
- )
- (setq level (nth 0 vals)
- indent (nth 1 vals))
-
- (setq this-indent
- (max 0 ; Make sure the indentation is at least to column 0
- (* sql-indent-offset
- (if (< level 0)
- 0
- level))))
-
- (if sql-indent-debug
- (progn
- (setq line (1+ line))
- (message "Line %3d; level %3d; indent was %3d; at %d" line level indent (point))))
-
- (beginning-of-line)
- (if (and (not (looking-at "^\\s-*$")) ; Leave blank lines alone
- (not (sql-indent-is-string-or-comment)) ; Don't mess with comments or strings
- (/= this-indent (current-indentation))) ; Don't change the line if already ok.
-
- (indent-line-to this-indent)
- )
-
- (end-of-line)
- )
- )
- )
- )
-
-(defun sql-indent-line ()
- "Indent current line in an SQL statement."
- (interactive)
- (let* ((pos (point))
- (indent-info (sql-indent-level-delta))
- (level-delta (nth 0 indent-info))
- (prev-indent (nth 1 indent-info))
- (this-indent (max 0 ; Make sure the indentation is at least 0
- (+ prev-indent
- (* sql-indent-offset
- (nth 0 indent-info)))))
- )
-
- (if sql-indent-debug
- (message "SQL Indent: level delta: %3d; prev: %3d; this: %3d"
- level-delta prev-indent this-indent))
-
- (save-excursion
-
- (beginning-of-line)
-
- (if (and (not (looking-at "^\\s-*$")) ; Leave blank lines alone
- (not (sql-indent-is-string-or-comment)) ; Don't mess with comments or strings
- (/= this-indent (current-indentation))) ; Don't change the line if already ok.
- (indent-line-to this-indent))
- )
- )
- )
-
-(add-hook 'sql-mode-hook
- (function (lambda ()
- (make-local-variable 'indent-line-function)
- (setq indent-line-function 'sql-indent-line))))
-
-(provide 'sql-indent)
-
-;;; sql-indent.el ends here
--- /dev/null
+.elpaignore
+.gitignore
+.travis.yml
+test-data
+sql-indent-test.el
+*~
+*.elc
--- /dev/null
+# -*- mode: conf -*-
+sql-indent-autoloads.el
+sql-indent-pkg.el
+*.elc
+*~
+\#*\#
+.\#*
--- /dev/null
+language: emacs-lisp
+
+# Require sudo because Emacs needs to disable ASLR to dump, only possible on
+# sudo-enabled environment. See also
+# https://github.com/travis-ci/travis-ci/issues/9061
+# https://github.com/flycheck/emacs-travis/issues/13
+
+sudo: required
+dist: trusty
+
+cache:
+ - directories:
+ # Cache stable Emacs binaries (saves 1min per job)
+ - "$HOME/emacs/"
+
+# Allow Emacs snapshot builds to fail and don’t wait for these as they can
+# take a looooong time
+
+matrix:
+ fast_finish: true
+ allow_failures:
+ - env: EMACS_VERSION=snapshot
+env:
+ - EMACS_VERSION=26.2
+before_install:
+ # Configure $PATH: Executables are installed to $HOME/bin
+ - export PATH="$HOME/bin:$PATH"
+ # Download the makefile to emacs-travis.mk
+ - wget 'https://raw.githubusercontent.com/alex-hhh/emacs-travis/master/emacs-travis.mk'
+ # Install Emacs (according to $EMACS_VERSION)
+ - make -f emacs-travis.mk install_emacs
+install:
+ # Install your dependencies
+script:
+ - emacs -batch -Q --no-site-file -L . -f batch-byte-compile *.el
+ - emacs -batch -Q --no-site-file -L . -l sql-indent-test.el -f ert-run-tests-batch-and-exit
--- /dev/null
+# If you have a question or found a bug
+
+To ask a question about the package, please please [create an
+issue][gh-issue-link] containing a description of what you are trying to do
+and consider providing sample SQL code if appropriate. There is also a
+[manual](./sql-indent.org) which provides information about customizing the
+indentation rules.
+
+If you found a bug in the SQL indentation code, or don't know how to configure
+the indentation rules to suit your preferences, please [create an
+issue][gh-issue-link]. Please provide a sample SQL code snippet that
+demonstrates the problem.
+
+# Submitting code changes
+
+The preferred way to accept contributions is to submit a pull request using
+GitHub. Before doing so, you need to be aware of the copyright assignment
+requirements and the automated test-suite. These are detailed in the sections
+below.
+
+## Copyright Assignment
+
+This package is part of [GNU ELPA][elpa-link] and it is subject to the GNU
+[Copyright Assignment][copy-papers-link] policy. Any [legally
+significant][legally-link] contributions can only be accepted after the author
+has completed their paperwork. Please see [the request form][request-link] if
+you want to proceed with the assignment.
+
+## Automated test suite
+
+There's an automated test suite which is used to ensure we don't re-introduce
+bugs that that were already fixed. If you fix the problem with the
+indentation, please provide an automated test for your fixes and add it to the
+test suite. The "Commentary" section in the
+[sql-indent-test.el](./sql-indent-test.el) file contains a description on how
+to add and run tests.
+
+## Other considerations for the pull request
+
+In your pull request, please provide a clear description of what the changes
+do and add a sample SQL snippet that illustrates the problem being solved.
+
+Once you submit the pull request, an automated build will start and will run
+the unit tests, please verify that the build succeeds and fix any issues if
+the build failed.
+
+[elpa-link]: http://elpa.gnu.org/packages/
+[copy-papers-link]: http://www.gnu.org/prep/maintain/html_node/Copyright-Papers.html
+[legally-link]: http://www.gnu.org/prep/maintain/html_node/Legally-Significant.html#Legally-Significant
+[request-link]: http://git.savannah.gnu.org/cgit/gnulib.git/tree/doc/Copyright/request-assign.future
+[gh-issue-link]: https://github.com/alex-hhh/emacs-sql-indent/issues
+
--- /dev/null
+2019-06-20 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Update version to 1.4
+
+2019-06-08 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.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
+
+2019-04-18 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Recognize more types of begin transaction statements (fixes #84)
+
+ "begin" follwed by a semicolon, "begin work" and "begin transaction" are
+ considered statements and not block start delimters.
+
+2019-03-31 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Don't recognize select keywords in comments, fixes #83
+
+ when searching for select keywords for anchors, don't stop inside
+ comments -- this just uses `sqlind-search-backward`, which makes sure
+ that the position that is found is at the same level as the starting
+ position.
+
+ Also fixed a bug in `sqlind-same-level-statement` which missed that the
+ anchor was inside a comment.
+
+2019-03-15 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ recognize elseif in addition to elif #81
+
+2019-03-15 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ don't be confused by drop function or procedure statements #80
+
+2019-03-10 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Apply advices of `checkdoc' (#77)
+
+ * sql-indent.el: spelling and other documentation fixes.
+
+2019-03-10 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Fix `sqlind-comment-end' regexp. (#79)
+
+ * sql-indent.el: Fix the regexp detected by\r
+ trawl (https://github.com/mattiase/trawl).
+
+2019-03-01 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ also fix sqlind-end-statement-regexp
+
+2019-03-01 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Fix regexp bug in `sqlind-good-if-candidate` (fixes #76)
+
+2019-02-20 Alex Harsányi <alex-hhhNOSPAMusers.noreply.github.com>
+
+ Recognize more create keywords (#75)
+
+
+
+2018-12-13 Alex Harsányi <alex-hhhNOSPAMusers.noreply.github.com>
+
+ Recognize FULL JOIN, UNION ALL and EXCEPT keywords #73 (#74)
+
+ * sql-indent.el (sqlind-select-clauses-regexp): match "UNION\r ALL" and
+ "EXCEPT" as select clauses.\r
+ (sqlind-select-join-regexp): match FULL JOIN keywords\r
+ (sqlind-lineup-joins-to-anchor): use `sqlind-select-join-regexp`\r to
+ match all join keywords.
+
+2018-12-13 Alex Harsányi <alex-hhhNOSPAMusers.noreply.github.com>
+
+ Recognize `select-join-condition` syntax inside nested statements #70
+ (#71)
+
+ Updated syntax parser to recognize some JOIN condition keywords as\r
+ `select-join-condition` syntax, even inside nested statements.\r
+ Updated some non-related tests (m, pr17 and pr68), as this syntax was
+ not\r recognized when these tests were written. While this change will
+ modify the\r indentation in such cases, it allows more refined control
+ over the indention.\r
+
+ * sql-indent.el (sqlind-join-condition-regexp): keywords that\r
+ constitute JOIN conditions\r
+ (sqlind-syntax-in-select): match `sqlind-join-condition-regexp`\r when
+ looking for join condition keywords\r
+ (sqlind-refine-syntax): try to refine a\r
+ `nested-statement-continuation into a `select-join-condition` if\r there
+ is a JOIN in the nested statement.
+
+2018-12-11 Alex Harsányi <alex-hhhNOSPAMusers.noreply.github.com>
+
+ `sqlind-search-backward` finds things at the same nesting level (#68)
+ (#69)
+
+ * sql-indent.el (sqlind-search-backward): use\r
+ `sqlind-same-level-statement` to check if the position is at the\r same
+ nesting level as the starting point\r
+
+ * (sqlind-same-level-statement): fix to check that the point has the\r
+ same nesting, in-comment and in-string status as start.\r
+
+2018-10-25 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ update version to 1.3
+
+2018-10-23 Alex Harsányi <alex-hhhNOSPAMusers.noreply.github.com>
+
+ Fix up bad detection of $$ delimiters in PostgreSQL dialect (#67)
+
+ this resulted in severe performance degradation for the indent function
+ and\r bad anchor for the $$ blocks.\r
+
+ * sql-indent.el (sqlind-maybe-create-statement): strip off any\r parens
+ off the function name.\r
+ (sqlind-maybe-$$-statement): re-wrote the function to avoid\r calling
+ `sqlind-beginning-of-block` recursively.\r
+ (sqlind-start-block-regexp): fixup $$ matching
+
+2018-10-09 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ don't recognize "begin transaction" as a block
+
+ "begin transaction;" is a statement, and the code used to confuse the
+ "begin" with a block begin, breaking some indentation.
+
+2018-07-12 Alex Harsányi <alex-hhhNOSPAMusers.noreply.github.com>
+
+ recognize implicit JOIN clauses (fixes #64) (#65)
+
+ ... these are JOIN clauses without an explicit INNER, LEFT, RIGHT, etc.
+
+2018-05-07 Stefan Monnier <monnierNOSPAMiro.umontreal.ca>
+
+ * sql-indent.el: Add `package-requires` for cl-lib
+
+2018-05-06 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ update to version 1.2
+
+2018-02-26 Alex Harsányi <alex-hhhNOSPAMusers.noreply.github.com>
+
+ Recognize MySQL optional clauses, #60 (#61)
+
+ (sqlind-maybe-skip-mysql-create-options): skip over optional clauses
+ between\r CREATE and the thing being created -- this allows correct
+ identification of\r the create statement\r
+
+ (sqlind-maybe-create-statement): skip over any "if not exist" keywords
+ between\r the thing being created and its name -- this allows correct
+ identification of\r the view or table name.\r
+
+ (sqlind-refine-syntax): refine SELECT statements inside table
+ declarations, as\r in "CREATE TABLE AS SELECT..."\r
+ also recognize tabs as whitespace syntax in various regexps.\r
+ add tests, also update the `if-exists.sql` test as the view name is
+ correctly\r determined now.
+
+2018-02-13 Stefan Monnier <monnierNOSPAMiro.umontreal.ca>
+
+ * sql-indent.el: Don't require cc-defs.
+
+ (sqlind-indent-comment-start): Use line-end-position.
+ (sqlind-indent-select-column, sqlind-indent-select-table)
+ (sqlind-lineup-to-clause-end): Use eolp.
+
+2018-02-11 Stefan Monnier <monnierNOSPAMiro.umontreal.ca>
+
+ sql-indent.el: Fix Author: email format
+
+2018-01-30 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Add .elpaignore file
+
+ Files and patterns listed here will be ignored when the ELPA package is
+ assembled.
+
+2018-01-28 Alex Harsányi <alex-hhhNOSPAMusers.noreply.github.com>
+
+ Fix travis build. (#58)
+
+ See also\r
+
+ * https://github.com/travis-ci/travis-ci/issues/9061\r
+ * https://github.com/flycheck/emacs-travis/issues/13\r
+
+2018-01-28 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Update sql-indent.el to version 1.1
+
+ Updated / clarified some of the documentation as well
+
+2018-01-06 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ add explanation on how `sqlind-beginning-of-block` works
+
+2018-01-06 Alex Harsányi <alex-hhhNOSPAMusers.noreply.github.com>
+
+ Recognize PostgreSQL $$ quote syntax, #54 (#55)
+
+ Function bodies in PostgreSQL are defined between '$$' markers. The
+ code now\r recognizes these as begin/end statements. There's a test
+ file,\r
+ `test-data/pr54.sql`, specifying the cases that are correctly detected.\r
+ PostgreSQL support likely needs more work.
+
+2018-01-04 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Fix various typos here and there. (#57)
+
+
+
+2018-01-04 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Fix typo in sql-indent.org. (#56)
+
+
+
+2018-01-03 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Updated documentation
+
+ * Simplified CONTRIBUTING.md -- removed information that is
+ available
+ elsewhere
+
+ * Fixed a type in the user manual, `sql-indent.org` specifying the
+ wrong hook
+ name for the customization
+
+ * Clarified test naming conventions in `sql-indent-test.el`
+
+2018-01-03 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Correct indentation of column continuation with operators. (#53)
+
+ * Correct indentation of column continuation with operators.\r
+ Correct from\r
+ ```sql\r select my_col || chr(10)\r
+ || '-*-' data_type,\r
+ x\r from dual;\r
+ ```\r
+ to\r
+ ```sql\r select my_col || chr(10)\r
+ || '-*-' data_type,\r
+ x\r from dual;\r
+ ```\r
+
+ * sql-indent.el\r
+ (sqlind-column-definition-start): find the first column of the\r
+ statement.\r
+ (sqlind-use-anchor-indentation): use the indentation of the anchor\r
+ instead of the indentation of the line containing the anchor.\r
+ (sqlind-adjust-operator): search the column start in\r
+ select-column-continuation.\r
+
+ * Add test for PR#53.\r
+
+ * Simplify code.\r
+
+ (sqlind-column-definition-start): avoid unnecessary (unless (while
+ ...)).\r
+ (sqlind-adjust-operator): avoid complex goto-char.\r
+
+2017-12-16 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Correct the adjustment of indentation for operators in select (#52)
+
+ * Correct the adjustment of indentation for operators in select\r
+ With this fix the following statement is propely indented\r
+
+ ``` sql\r select mt.myVar\r
+ || '-*-' myVarLabel,\r
+ myVar2\r from myTable mt;\r
+ ```\r
+ instead of\r
+
+ ``` sql\r select mt.myVar\r
+ || '-*-' myVarLabel,\r
+ myVar2\r from myTable mt;\r
+ ```\r
+
+ * sql-indent.el: Use sqlind syntax table in adjustments functions.\r
+ (sqlind-adjust-operator): Use sqlind syntax table.\r
+
+ * Add test to pr52\r
+
+2017-12-16 Alex Harsányi <alex-hhhNOSPAMusers.noreply.github.com>
+
+ Add CONTRIBUTING.md file (#51)
+
+
+
+2017-12-12 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Add new indentation function for select columns. (#50)
+
+ * Add new indentation function for select columns.\r
+
+ * sql-indent-left.el: Improve indentation of select columns\r
+ (sqlind-indent-select-keywords): tweak indentation after DISTINCT,\r
+ UNIQUE or TOP keywords.\r
+
+ * Add tests for pr50, correct sqlind-indentation-left-offsets-alist.\r
+
+ * sql-indent-left.el: correct sqlind-indent-select-keywords\r
+ (sqlind-indent-select-keywords): avoid error when no keyword found.\r
+
+ * sql-indent-test.el: add test for pr50.\r
+
+ * Add `sqlind-indent-select-column-alt'.\r
+ Clone `sqlind-indent-select-column' into
+ `sqlind-indent-select-column-alt'\r to allow alignment of select colums
+ with KEYWORDS.\r
+
+ * sql-indent-left.el: Change setup to use
+ `sqlind-indent-select-column-alt'\r
+ (sqlind-indent-select-column-alt): extends
+ `sqlind-indent-select-column'.\r
+
+ * Checkdoc style corrections.\r
+ sql-indent-left.el: apply chechdoc advices.\r
+
+2017-12-02 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Update README with instructions on how to install from GNU ELPA
+
+ * README.md: add instructions for installing from GNU ELPA
+
+ * sql-indent.el: reference `sql-indent.org` in the commentary section
+
+2017-11-30 Stefan Monnier <monnierNOSPAMiro.umontreal.ca>
+
+ Fix up copyrights for GNU ELPA, plus minor tweaks
+
+ * .gitignore: Add -autoloads and -pkg files.
+
+ * sql-indent-test.el, sql-indent-left.el: Fix copyright header.
+
+ * sql-indent.el: Fix copyright header. Don't require `align`.
+ (sqlind-minor-mode-map): New keymap.
+ (sqlind-minor-mode): Don't modify sql-mode's keymap. Use
+ kill-local-variable.
+
+2017-10-13 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Correct detection of labels in `sqlind-maybe-loop-statement'. (#49)
+
+ Correct detection of labels in `sqlind-maybe-loop-statement'
+
+2017-10-09 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Correct detection of labels in `for` or `while` loops. (#48)
+
+ Correct detection of labels in `for` or `while` loops.
+
+2017-09-21 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Introduce a nested-statement-close syntax
+
+ This is recognized inside a nested statement when the line to be
+ indented starts with a closing bracket. This is intended to replace
+ `sqlind-lineup-close-paren-to-open` and
+ `sqlind-lineup-close-paren-to-open-indentation` indentation helpers
+ which had to be added to too many syntactic symbols.
+
+ With the new syntax symbol, using `sqlind-use-anchor-indentation` will
+ lineup hanging close brackets with the start of the statement and
+ `sqlind-use-anchor-indentation` will line them up with the actual
+ opening bracket itself.
+
+2017-09-19 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ new alignment rule for issue #47
+
+2017-09-19 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ fixup refining a nested-statement-continuation (#46)
+
+ when the nested statement continuation had a comment at the start, any
+ select statements inside it were missed.
+
+2017-09-18 Brendan Rocks <brendan-rNOSPAMusers.noreply.github.com>
+
+ Minor documentation tweaks (#44)
+
+ Fix installation command in documentation and other typos\r
+
+ `install-package-from-buffer` makes more linguistic sense, but\r
+ `package-install-from-buffer`, seems to be the appropriate\r command
+ (Emacs 25.2)\r
+
+
+2017-09-18 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ new indentation helper function and clarified documentation
+
+ added `sqlind-lineup-close-paren-to-open-indentation` function to assist
+ with issue #43
+
+2017-08-19 Alex Harsányi <alex-hhhNOSPAMusers.noreply.github.com>
+
+ Don't be confused by names that contain keywords (#42)
+
+ Don't be confused by names that contain keywords\r
+ for example, the "FOR" in "V_SPORT_ZONE_FOR_SESSION" is not a keyword.\r
+
+ * Add test cases for PR42\r
+
+2017-08-19 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Fix example in documentation
+
+2017-08-09 Alex Harsányi <alex-hhhNOSPAMusers.noreply.github.com>
+
+ Travis build and test support (#41)
+
+ Add Travis CI support to the repository
+
+2017-08-09 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Recognize MsSQL directives and declarations (#40)
+
+2017-08-02 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Fix detection of select column start and LIMIT keyword #39
+
+ * sql-indent.el (sqlind-column-definition-start): use LIMIT as the
+ bounds for `sqlind-same-level-statement`, so we search between the start
+ of the SELECT and the current point.
+ (sqlind-select-clauses-regexp): recognize LIMIT as a select clause
+ keyword.
+
+2017-07-30 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Correct display of lighter when minor-mode is enabled. (#38)
+
+ Add an space before text to avoid it to stick on previous word in
+ modeline.
+
+2017-07-24 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Allow detection of user name in package, procedure or function. (#37)
+
+ * Allow detection of user name in package, procedure or function.\r
+ Avoid `mismatched end label for package (1 104)` message whith and sql\r
+ of this form:\r
+
+ ```sql\r create or replace package my_user.my_wonderfull_package as\r
+ function my_amazing_func return number;\r end my_wonderfull_package;\r
+ /\r
+ ```\r
+
+ * Add tests to PR #37.\r
+
+2017-07-21 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Try to correct 'bad closing for begin block' message. (#36)
+
+ Detection of for .. loop inside another statement\r
+ see "test-data/pr36.sql" for an example.\r
+
+2017-07-15 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Add %type, %rowtype and table prefix in function's return type. (#33)
+
+ * Add %type, %rowtype and table prefix in function's return type.\r
+ Allow correct indenting after and function declaration like bellow:\r
+ declare\r
+ function dummy return my_user.my_table%rowtype;\r
+ function dummy2 return my_user.my_table.my_col%type;\r
+ var dummy3 number;\r begin\r
+ null;\r end;\r
+
+ * Add tests cases for PR #33.\r
+
+2017-07-15 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Improve left indentation of case statement. (#34)
+
+
+
+2017-06-29 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Don't be fooled by "if exists" statements
+
+ "IF" keywords in things like "drop index if exists" statements are no
+ longer considered statement starts.
+
+2017-06-29 Alex Harsányi <alex-hhhNOSPAMusers.noreply.github.com>
+
+ Performance improvements (#31)
+
+ * Establish a limit for syntactic information search\r
+ See also issue #26\r
+
+ `sqlind-syntax-of-line' will use `sqlind-begining-of-directive' to
+ establish a\r search limit for the current syntax. This avoids calling\r
+ `sqlind-begining-of-directive' repeatedly.\r
+ This changes the indentation anchor for toplevel statements when
+ directives\r are present in the file. Unit test data updated
+ accordingly.\r
+
+ * sql-indent.el (sqlind-search-limit): new variable\r
+ (sqlind-beginning-of-statement-1): avoid searching before\r
+ `sqlind-search-limit'\r
+ (sqlind-beginning-of-statement): use `sqlind-search-limit'\r
+ (sqlind-maybe-loop-statement): fix mispelling of throw symbol\r
+ (sqlind-beginning-of-block): limit search to `sqlind-search-limit'\r
+ (sqlind-syntax-of-line): bind `sqlind-search-limit' to\r
+ `sqlind-begining-of-directive'\r
+
+ * Fix spelling of `sqlind-beginning-of-directive`\r
+
+2017-06-20 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Performance improvements (#26)
+
+ * sql-indent.el (sqlind-backward-syntactic-ws)
+ (sqlind-forward-syntactic-ws, sqlind-search-backward): rewrite code to
+ avoid catch/throw forms
+ (sqlind-match-string): make it a `defsubst'
+ (sqlind-beginning-of-statement): avoid calling
+ `sqlind-begining-of-directive' when not needed
+ (sqlind-syntax-of-line): avoid unnecessary goto-char calls, and replace
+ `parse-partial-sexp' with `syntax-ppss'
+
+2017-06-15 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Moved most of the README.md contents into sql-indent.org
+
+ ... this is the beginning of a sql-ident manual. The ORG format should
+ make it easier to browse in Emacs, and Github also renders it quite
+ nicely.
+
+2017-06-15 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Avoid infinite loop in `sqlind-syntax-in-case`
+
+ Updated the test case with code that triggered the bug
+
+2017-06-15 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Detect end of exception block correctly (#30)
+
+
+
+2017-06-13 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ fix anchor point for case statement syntax (#29)
+
+ code that was looking for the "when" keyword was moving the point,
+ resulting in the incorrect anchor point being used for '(in-block case
+ ...) syntax. Updated tests to reflect fixed code.
+
+ Also added `sqlind-syntax-keyword` helper function, to be used by
+ indentation functions.
+
+2017-06-13 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Fix exception being a type as well as a keyword (#28)
+
+ "exception" is both a keyword and a datatype. Add code to differentiate
+ between the two. Only the keyword is a block start. Updated test files
+ to reflect that.
+
+2017-06-12 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ fix syntax detection in exception blocks and case statements (#28 and
+ #29)
+
+ Exception statements with multiple when clauses are correctly recognized
+ and classified. Case statements with multi-lone when clauses are also
+ correctly identified. Added unit tests for both cases.
+
+ Some refactoring was necessary to allow recursive syntax refinement
+ (`sqlind-refine-syntax`)
+
+2017-06-12 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Fix incorrect match for end keyword in sqlind-end-statement-regexp
+
+2017-06-08 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Fix bug with wrong regexp and throw clause
+
+ a wrong "end" keyword regexp would match non-keywords that ended with
+ the string "end", causing problems
+
+ also fixes a misspelling for a throw clause
+
+2017-06-08 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Various minor improvements on left align SQL statments. (#22)
+
+ * Various minor improvements on left align SQL statments.\r
+
+ * Update pr17-io-left after updating sql-indent-left setup.\r
+
+2017-06-03 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Remove execution bits on sql-indent-left.el (#25)
+
+
+
+2017-06-03 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Recognize assignments as statement starts (#24)
+
+2017-06-01 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Update test for issue #19 to be more comprehensive
+
+2017-06-01 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Merge branch 'master' of github.com:alex-hhh/emacs-sql-indent
+
+2017-06-01 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Update unit test suite to run independent of user settings
+
+ Previously, the test suite depended on whether the tabs are expanded
+ into spaces or not and other user settings.
+
+2017-05-31 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Exclude equals inside strings from alignments. (#20)
+
+ Update align rules to ignore equal signs inside strings (#20)
+
+2017-05-31 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Add a rule to align pipes (||). (#21)
+
+ Align rules for string concatenation operators (#21)
+
+2017-05-30 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Correctly recognize the begin block of a package (#19)
+
+2017-05-29 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Correctly detect syntax for cursor statements (fix #19)
+
+ Cursors are correctly detected as beginning of a statement. Also an
+ optional type for the cursor is recognized correctly.
+
+ Also fixed block navigation for package names (so package end statements
+ are correctly recognized)
+
+ Added tests for the above cases.
+
+2017-05-26 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Identify syntax for CASE expressions in assignment (fixes #18)
+
+ Also added unit tests that shows/tests the types of CASE expressions
+ that can show up in PL/SQL.
+
+2017-05-25 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ use cl-lib instead of cl and provide some helpers
+
+ the cl library is deprecated in Emacs, to be replaced with cl-lib (which
+ has the same functions, but with the "cl-" prefix)
+
+ Also provided some helper functions to access syntactic context,
+ hopefully this will simplify the code somewhat.
+
+2017-05-24 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Fix checkdoc errors in sql-indent.el
+
+2017-05-24 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ moved contents of customize-indentation.md into README.md
+
+2017-05-24 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Recognize syntax of CASE statements which are not nested
+
+ Based on an example in https://github.com/bsvingen/sql-indent/issues/1
+
+ Also added test case for this syntax type.
+
+2017-05-24 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Add test cases for sample indentation mode
+
+ Based on a sample SQL indentation example provided by the SQL mode
+ maintainer.
+
+2017-05-24 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ updated .gitignore
+
+2017-05-23 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Add a test suite for sql-indent
+
+2017-05-23 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Updated installation and customization documentation
+
+2017-05-23 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Fix indentation.
+
+ Previous version didn't work.
+
+2017-05-23 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Compilation error ...
+
+2017-05-23 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Correct indentation of function's declaration in package specs.
+
+ Detect end of function declaration in package spec.
+
+2017-05-22 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ sqlind-begining-of-directive -- another approach to disambiguate SET
+
+2017-05-22 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ sqlind-begining-of-directive -- another approach to disambiguate SET
+
+2017-05-22 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ sqlind-adjust-operator -- fix up string concatenation cases
+
+ The operator correctly determines the position of the left hand operand
+ for the binary operator. For example the code below is indented
+ correctly now:
+
+ ```sql select col1, 'abc def'--comment
+ || 'abc'
+ || 'def'
+
+ ```
+
+2017-05-22 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Renamed some indentation functions for consistency
+
+ * `sqlind-adjust-and-or-left` renamed to
+ `sqlind-left-justify-logical-operator`
+
+ * `sqlind-adjust-and-or-right` renamed to
+ `sqlind-right-justify-logical-operator`
+
+2017-05-22 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ silence byte compiler warnings
+
+2017-05-22 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ sqlind-begining-of-directive -- disambiguate Oracle SET keyword
+
+ The SET keyword in Oracle SQLPlus is a directive as well as part of the
+ SQL UPDATE statement. `sqlind-begining-of-directive' will correctly
+ identify uses of SET which are directives.
+
+2017-05-20 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Start workgin on aligning keywords left or right. (#14)
+
+ Provide sample customization for the SQL indentation engine\r
+ Some extra indentation helper functions are also provided and
+ documented.
+
+2017-05-14 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ sqlind-beginning-of-statement-1 -- don't look inside comments, fixes #15
+
+2017-05-14 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Separate indentation of AND, OR into a separate rule.
+
+2017-05-11 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Use default value of indent-line-function when deactivating minor mode.
+
+ As the user could change the value of indent-line-function, we could not
+ reset it to 'indent-relative.
+
+2017-05-11 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Create a `sqlind-minor-mode` to toggle syntactic indentation
+
+ Also updated commentary section with customization starting points.
+
+2017-05-10 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ update alignment rules to handle variable declarations with assignments
+
+ This is further work on pull request #13
+
+2017-05-10 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Merge branch 'master' of github.com:alex-hhh/emacs-sql-indent
+
+2017-05-10 Alex Harsányi <alex-hhhNOSPAMusers.noreply.github.com>
+
+ Merge pull request #13 from PierreTechoueyres/pte/sqlind-align-rules
+
+ Add support for assignment and arrow (=>) in align.
+
+2017-05-10 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ fixup sqlind-lineup-joins-to-anchor
+
+2017-05-10 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Add a handful if indentation helpers
+
+ They are not used directly, but they can be used to construct custom
+ indentation tables.
+
+2017-05-10 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ classify select columns start start with a comma as select-column
+
+ This helps with indenting SQL statements that use a comma at the start
+ of the column, like this:
+
+ SELECT col1
+ ,col2
+ FROM ...
+
+ This is also consistent with how we classify select-table syntaxes
+
+2017-05-09 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Add support for assignment and arrow (=>) in align.
+
+ Allow to align some keywords in sql statements. Ex:
+
+ declare
+ function dummy(p_param_1 in varchar2,
+ p_param_2 in out varchar2,
+ p_param_2 out varchar2)
+ return boolean;
+ end dummy;
+
+ function dummy_2(p_param_1 out varchar2,
+ p_param_2 out varchar2,
+ p_param_2 out varchar2)
+ return boolean;
+
+ function dummy_3(p_param_1 varchar2,
+ p_param_2 in varchar2,
+ p_param_2 out varchar2)
+ return boolean;
+
+ var1 boolean := true;
+ var2 number := 1;
+ var42 varchar2(1) := 'Y'; begin
+ if dummy(p_param_1 => val1,
+ p_param_10 => val10) then
+ null;
+ end if; end;
+ /
+
+2017-05-07 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ install a separate syntax table for syntax parsing
+
+ keep the sql-mode syntax table unchanged, but use a special one during
+ syntactic parsing. This is done so that navigation over syntactic
+ elements is simpler for parsing, but stays reasonable while the user
+ edits the file.
+
+ For example, the string "table.column" will be considered one symbol for
+ indentation purposes, but two symbols for editing (so an M-b will move
+ to the dot)
+
+2017-05-07 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ put our own syntax table into sql-mode
+
+2017-05-07 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ move default indentation offsets in separate constant
+
+ `sqlind-indentation-offsets-alist' is now buffer local and it is simpler
+ to override
+
+2017-05-06 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Merge branch 'master' of github.com:alex-hhh/emacs-sql-indent
+
+2017-05-06 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Add initial align rules for SQL statements
+
+2017-05-05 Alex Harsányi <alex-hhhNOSPAMusers.noreply.github.com>
+
+ Merge pull request #6 from PierreTechoueyres/pte/add-keywors
+
+ Add new keywords in `sqlind-sqlplus-directive'.
+
+2017-05-05 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ don't move cursor when indenting the line
+
+ previous simplified too much
+
+2017-05-04 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Add new keywords in `sqlind-sqlplus-directive'.
+
+ Add Oracle's keywords to start scripts inside a script.
+
+2017-05-04 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Updated README file
+
+2017-05-04 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Use `indent-line-to` in `sqlind-indent-line`
+
+2017-03-23 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Indent nested if statements correctly, fixes #4
+
+ `sqlind-begining-of-block` and `sqlind-begining-of-statement` recognize
+ IF statements and stop at the right point so that syntactic info is
+ created correctly.
+
+2017-03-22 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ fix parsing the "end if" block when an "elsif" is present
+
+2017-01-02 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Add new keywords in `sqlind-sqlplus-directive'.
+
+2016-12-30 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ update markdown formatting
+
+2016-12-30 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Added documentation on how to customize indentation
+
+2016-12-28 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Enable lexical-binding.
+
+ Enable lexical-binding and suppress compiler warning about unused
+ variables.
+
+2016-10-07 alex-hhh <alex-hhhNOSPAMusers.noreply.github.com>
+
+ Merge pull request #1 from PierreTechoueyres/pte/start-with-connect-by
+
+ Add support for connect by/start with.
+
+2016-10-06 Pierre Téchoueyres <pierre.techoueyresNOSPAMfree.fr>
+
+ Add support for connect by/start with.
+
+ Add support for Oracle SQL start with / connect by. Align them like
+ group by.
+
+2016-05-21 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ fixup parsing of case clauses
+
+2015-12-23 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ Add comments and headers to make it a proper pakage
+
+ This file can now be installed using package-install and friends.
+
+2015-12-21 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ updated README file
+
+2015-12-21 Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+
+ initial revision
+
+2015-12-21 alex-hhh <alex-hhhNOSPAMusers.noreply.github.com>
+
+ Initial commit
+
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ {one line to give the program's name and a brief idea of what it does.}
+ Copyright (C) {year} {name of author}
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ {project} Copyright (C) {year} {fullname}
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
--- /dev/null
+# Syntax based indentation for SQL files for GNU Emacs
+
+[](https://travis-ci.org/alex-hhh/emacs-sql-indent)
+
+sql-indent.el is a GNU Emacs minor mode which adds support for syntax-based
+indentation when editing SQL code: TAB indents the current line based on the
+syntax of the SQL code on previous lines. This works like the indentation for
+C and C++ code.
+
+The package also defines align rules so that the `align` function works for
+SQL statements, see `sqlind-align-rules` for which rules are defined. This
+can be used to align multiple lines around equal signs or "as" statements,
+like this.
+
+`sqlind-minor-mode` together with the align rules can assist in writing tidy
+SQL code or formatting existing SQL code. The indentation rules are
+customizable and they can be adapted to match your coding style.
+
+See the [manual](./sql-indent.org) for more details.
+
+# Installation
+
+You can install a released version of this package
+from [GNU ELPA](http://elpa.gnu.org/packages/sql-indent.html), by running the
+following commands in Emacs:
+
+ M-x package-install RET sql-indent RET
+
+To install sql-indent from this repository, open the file `sql-indent.el` in
+Emacs and type:
+
+ M-x package-install-from-buffer RET
+
+The syntax-based indentation of SQL code can be turned ON/OFF at any time by
+enabling or disabling `sqlind-minor-mode`:
+
+ M-x sqlind-minor-mode RET
+
+To enable syntax-based indentation for every SQL buffer, you can add
+`sqlind-minor-mode` to `sql-mode-hook`. First, bring up the customization
+buffer using the command:
+
+ M-x customize-variable RET sql-mode-hook RET
+
+Than, click on the "INS" button to add a new entry and put "sqlind-minor-mode"
+in the text field.
+
--- /dev/null
+;;; sql-indent-left.el --- configuration options to indent sql -*- lexical-binding: t -*-
+
+;; Copyright (C) 2017 Free Software Foundation, Inc
+
+;; Filename: sql-indent-left.el
+;; Description:
+;; Author: pierre.techoueyresNOSPAMfree.fr
+;; Maintainer: pierre.techoueyresNOSPAMfree.fr
+;; Created:
+;; Version: pierre.techoueyresNOSPAMfree.fr
+;; Last-Updated:
+;; By:
+;; Update #: 0
+;; URL:
+;; Keywords: language sql indentation
+;; Compatibility:
+;;
+;; Features that might be required by this library:
+;;
+;; None
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3 of the License, or
+;; (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;; Commentary:
+;;
+;; Set configuration options to indent sql my way.
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;; Change log:
+;;
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; This program is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation; either version 3, or
+;; (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program; see the file COPYING. If not, write to
+;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
+;; Floor, Boston, MA 02110-1301, USA.
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;; Code:
+
+(require 'sql-indent)
+
+(defun indent-case-statement-items (syntax base-indentation)
+ "Look for a SYNTAX of ((block-start when) (in-block case \"\") ...)
+or ((block-start else) (in-block case \"\") ...)."
+ (let ((outer (sqlind-outer-context syntax)))
+ (if (and (eq 'in-block (sqlind-syntax-symbol outer))
+ (eq 'case (sqlind-syntax-keyword outer))
+ (eq 'block-start (sqlind-syntax-symbol syntax))
+ (memq (sqlind-syntax-keyword syntax) '(when else)))
+ (+ base-indentation sqlind-basic-offset)
+ base-indentation)))
+
+(defvar sqlind-indentation-right-offsets-alist
+ `((select-column-continuation sqlind-indent-select-column
+ sqlind-adjust-operator
+ sqlind-lone-semicolon)
+ (in-select-clause sqlind-lineup-to-clause-end
+ sqlind-adjust-operator
+ sqlind-right-justify-logical-operator
+ sqlind-lone-semicolon)
+ (in-delete-clause sqlind-lineup-to-clause-end
+ sqlind-adjust-operator
+ sqlind-right-justify-logical-operator
+ sqlind-lone-semicolon)
+ (in-insert-clause sqlind-lineup-to-clause-end
+ sqlind-adjust-operator
+ sqlind-right-justify-logical-operator
+ sqlind-lone-semicolon)
+ (in-update-clause sqlind-lineup-to-clause-end
+ sqlind-adjust-operator
+ sqlind-right-justify-logical-operator
+ sqlind-lone-semicolon)
+ ;; mandatory
+ (select-table-continuation sqlind-indent-select-table +
+ sqlind-lone-semicolon)
+ ;; rest picked up from the original indentation offsets
+ ,@sqlind-default-indentation-offsets-alist)
+ "Align sql code like this :
+
+clear columns
+set linesize 2500
+set trimout on trimspool on
+
+select atc.column_name,
+ atc.data_type,
+ data_length,
+ data_precision,
+ nullable,
+ data_scale,
+ nvl(substr(comments, 1, 100), atc.column_name) comments
+ from all_tab_columns atc,
+ all_col_comments acc
+ where atc.owner = acc.owner
+ and atc.table_name = acc.table_name
+ and atc.column_name = acc.column_name
+ and atc.owner = user
+ and atc.table_name = 'MY_TABLE'
+ and atc.column_name = p_column_name
+ and not exists (select 1
+ from all_tab_columns atc1,
+ all_col_comments acc1
+ where atc1.owner = acc1.owner
+ and atc1.table_name = acc1.table_name
+ and atc1.column_name = acc1.column_name
+ and atc1.owner = atc.owner
+ and atc1.table_name = atc.table_name
+ and acc1.column_name = acc.column_name)
+ ;
+
+delete from my_table mt
+ where col_1 = v_col1
+ and ( col_2 = v_col2
+ or col_3 = v_col3)
+ and col_42 = '42'
+ ;
+
+update my_table
+ set col1_has_a_long_name = value1,
+ col2_is_short = value2
+ where cond1 is not null
+ and ( col_2 = v_col2
+ or col_3 = v_col3)
+ and col_42 = '42'
+ ;
+
+insert into xyzxx
+ ( aaa, xxx, bbb, ccc,
+ ddd, eee, fff, ggg,
+ hhh )
+select aaa,
+ xxx,
+ max (m.b1) as bbb,
+ min (m.b1) as ccc,
+ coalesce (max (n.c2), 0)  as ddd,
+ coalesce (min (n.c2), 0)  as eee,
+ max (m.b1) over ( partition by c2
+ order by aaa desc ) as fff,
+ min (m.b1) over ( partition by c2
+ order by aaa desc ) as ggg,
+ avg (n.c2) as hhh
+ from (select * from (select aaa,
+ jjj + kkk  as b1,
+ row_number () over ( partition by qqq
+ order by rrr,
+ sss ) as rn
+ from mno)
+ where rn = 1) m
+ inner join (select aaa,
+ nnn + ooo as c2
+ from pqr) n
+ using (aaa),
+ group by aaa,
+ xxx
+ order by xxx desc,
+ aaa asc
+ ;")
+
+(defvar sqlind-indentation-left-offsets-alist
+ `((select-clause 0)
+ (insert-clause 0)
+ (delete-clause 0)
+ (update-clause 0)
+ (case-clause-item-cont 0)
+ (block-start indent-case-statement-items)
+ (begin-block 0)
+ (case-clause +)
+ (package +)
+ (package-body +)
+ (statement-continuation + sqlind-adjust-operator)
+ (nested-statement-open 1)
+ (nested-statement-continuation 1)
+ (nested-statement-close sqlind-use-anchor-indentation)
+ (string-continuation 0) ;; or should it be a beginning of line or aligned with the previous block ?
+ ;; Anyway. It's really *BAD* to continue a string across lines.
+ (select-column sqlind-indent-select-column-alt
+ sqlind-adjust-operator
+ sqlind-lone-semicolon)
+ (select-column-continuation sqlind-indent-select-column-alt
+ sqlind-adjust-operator
+ sqlind-lone-semicolon)
+ (in-select-clause sqlind-lineup-to-clause-end
+ sqlind-adjust-operator
+ sqlind-left-justify-logical-operator
+ sqlind-lone-semicolon)
+ (in-delete-clause sqlind-lineup-to-clause-end
+ sqlind-adjust-operator
+ sqlind-left-justify-logical-operator
+ sqlind-lone-semicolon)
+ (in-insert-clause +
+ sqlind-adjust-operator
+ sqlind-left-justify-logical-operator
+ sqlind-lone-semicolon)
+ (in-update-clause sqlind-lineup-to-clause-end
+ sqlind-adjust-operator
+ sqlind-left-justify-logical-operator
+ sqlind-lone-semicolon)
+ (select-table-continuation + sqlind-lone-semicolon)
+ ;; rest picked up from the original indentation offsets
+ ,@sqlind-default-indentation-offsets-alist)
+ "Align sql code like this :
+
+clear columns
+set linesize 2500
+set trimout on trimspool on
+
+select DISTINCT
+ atc.column_name,
+ atc.data_type,
+ data_length,
+ data_precision,
+ nullable,
+ data_scale,
+ nvl(substr(comments, 1, 100), atc.column_name) comments
+from all_tab_columns atc,
+ all_col_comments acc
+where atc.owner = acc.owner
+and atc.table_name = acc.table_name
+and atc.column_name = acc.column_name
+and atc.owner = user
+and atc.table_name = 'MY_TABLE'
+and atc.column_name = p_column_name
+and not exists (select 1
+ from all_tab_columns atc1,
+ all_col_comments acc1
+ where atc1.owner = acc1.owner
+ and atc1.table_name = acc1.table_name
+ and atc1.column_name = acc1.column_name
+ and atc1.owner = atc.owner
+ and atc1.table_name = atc.table_name
+ and acc1.column_name = acc.column_name)
+;
+
+delete from my_table mt
+where col_1 = v_col1
+and ( col_2 = v_col2
+ or col_3 = v_col3)
+and col_42 = '42'
+;
+
+update my_table
+set col1_has_a_long_name = value1,
+ col2_is_short = value2
+where cond1 is not null
+and ( col_2 = v_col2
+ or col_3 = v_col3)
+and col_42 = '42'
+;
+
+insert into xyzxx
+ ( aaa, xxx, bbb, ccc,
+ ddd, eee, fff, ggg,
+ hhh )
+select aaa,
+ xxx,
+ max (m.b1) as bbb,
+ min (m.b1) as ccc,
+ coalesce (max (n.c2), 0)  as ddd,
+ coalesce (min (n.c2), 0)  as eee,
+ max (m.b1) over ( partition by c2
+ order by aaa desc ) as fff,
+ min (m.b1) over ( partition by c2
+ order by aaa desc ) as ggg,
+ avg (n.c2) as hhh
+from (select * from (select aaa,
+ jjj + kkk  as b1,
+ row_number () over ( partition by qqq
+ order by rrr,
+ sss ) as rn
+ from mno)
+ where rn = 1) m
+ inner join (select aaa,
+ nnn + ooo as c2
+ from pqr) n
+ using (aaa),
+group by aaa,
+ xxx
+order by xxx desc,
+ aaa asc
+;")
+
+(defun sqlind-indent-select-column-alt (syntax base-indentation)
+ "Return the indentation for a column after a SELECT DISTINCT clause.
+
+SYNTAX is the syntax of the current line, BASE-INDENTATION is the
+current indentation, which we need to update.
+
+Like `sqlind-indent-select-column' but we try to align to the KEYWORD,
+but if we are the first column after the SELECT clause we simply
+add `sqlind-basic-offset'."
+ (save-excursion
+ (goto-char (sqlind-anchor-point syntax))
+ (when (looking-at "select\\s *\\(top\\s +[0-9]+\\|distinct\\|unique\\)?")
+ (if (match-beginning 1)
+ (goto-char (match-beginning 1))
+ (goto-char (match-end 0))))
+ (skip-syntax-forward " ")
+ (if (or (looking-at sqlind-comment-start-skip)
+ (looking-at "$"))
+ (+ base-indentation sqlind-basic-offset)
+ (current-column))))
+
+;;;###autoload
+(defun sqlind-setup-style-left ()
+ "Define an sql-indentation style where keywords are left aligned."
+ (interactive)
+ (setq sqlind-indentation-offsets-alist sqlind-indentation-left-offsets-alist))
+
+;;;###autoload
+(defun sqlind-setup-style-right ()
+ "Define an sql-indentation style where keywords are right aligned."
+ (interactive)
+ (setq sqlind-indentation-offsets-alist sqlind-indentation-right-offsets-alist))
+
+
+;;;###autoload
+(defun sqlind-setup-style-default ()
+ "Define an sql-indentation style where keywords are right aligned."
+ (interactive)
+ (setq sqlind-indentation-offsets-alist sqlind-default-indentation-offsets-alist))
+
+
+(provide 'sql-indent-left)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; sql-indent-left.el ends here
--- /dev/null
+;;; sql-indent-test.el --- Automated tests for sql-indent.el. -*- lexical-binding: t -*-
+
+;; Copyright (C) 2017 Free Software Foundation, Inc
+
+;; Author: Alex Harsanyi (AlexHarsanyiNOSPAMgmail.com)
+;; Created: 23 May 2017
+;; Keywords: languages sql
+;; Homepage: https://github.com/alex-hhh/emacs-sql-indent
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3 of the License, or
+;; (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This file defines tests for the sql-indent.el package. To run the tests,
+;; eval this file, than type:
+;;
+;; M-x ert RET "^sqlind-" RET
+;;
+;; Tests can also be run in batch mode using the following command:
+;;
+;; emacs -batch -Q --no-site-file -L . -l sql-indent-test.el -f ert-run-tests-batch-and-exit
+;;
+;; The above command used '-Q' and '--no-site-file options', making sure that
+;; the tests are run in a "standard" environment, regardless of what packages
+;; and settings are present in your personal init and site-init files.
+;;
+;;;; There are two types of tests,
+;;
+;; * SYNTAX CHECKS check if the syntax in an SQL file is correctly
+;; identified. These tests are independent of indentation preferences (see
+;; `sqlind-ert-check-file-syntax')
+;;
+;; * INDENTATION CHECKS check if a file is indented correctly for a set of
+;; rules. (see `sqlind-ert-check-file-indentation')
+;;
+;; Both types of tests work by having a sample SQL file with syntax and
+;; indentation data saved in an .eld files. These data files need to be
+;; prepared separately using `sqlind-collect-syntax-from-buffer' and
+;; `sqlind-collect-indentation-offsets-from-buffer'.
+;;
+;;
+;;;; PREPARING NEW TESTS
+;;
+;; To create a syntax check file, open an *ielm* buffer (M-x ielm RET) and
+;; run:
+;;
+;; (sqlind-collect-syntax-from-buffer (find-file-noselect "./test-data/pr7.sql"))
+;;
+;; The function will output a set of syntax definitions. Put these into an
+;; .eld file.
+;;
+;; To create an indentation offsets file, run:
+;;
+;; (sqlind-collect-indentation-offsets-from-buffer
+;; (find-file-noselect "./test-data/pr7.sql")
+;; sqlind-indentation-left-offsets-alist
+;; 2)
+;;
+;; The function will output a list of indentation offsets. Put these into an
+;; .eld file.
+;;
+;; See the end of file for examples on how to put together the actual tests.
+;;
+;;
+;;;; NAMING CONVENTION
+;;
+;; If there's a problem report on GitHub, use the issue number as the "tag"
+;; for the test. For example, a test that addresses issue number 30 will have
+;; a tag "pr30". If there is no issue on GitHub, use the pull request number.
+;; In this case you will have to create the pull request first, so you know
+;; the number, before you can add the tests -- sorry about that.
+;;
+;; With the issue or pull request tag the following files are created:
+;;
+;; * "test-data/pr30.sql" -- contains the sample SQL code that is verified
+;;
+;; * "test-data/pr30-syn.eld" -- contains the syntactic information for the ;;
+;; test (as generated by `sqlind-collect-syntax-from-buffer')
+;;
+;; * "test-data/pr30-io-XXX.eld" -- contains the indentation offsets for the
+;; indentation rules being tested (where XXX is a short name for those
+;; rules)
+;;
+;; * `sqlind-ert-pr30' -- is the name of the test function in this file. If
+;; there are multiple tests, append another tag (e.g. `sqlind-ert-pr30-syn',
+;; for the syntactic check test, `sqlind-ert-pr30-io-XXX' for the indentation
+;; offset test).
+;;
+
+;;; Code
+(require 'ert)
+(require 'sql-indent)
+(require 'sql-indent-left)
+
+\f
+;;................................................ test data preparation ....
+
+(defun sqlind-collect-syntax-from-buffer (buffer)
+ (let ((result '()))
+ (with-current-buffer buffer
+ ;; NOTE: we indent the buffer according to the default rules first, as
+ ;; this affects anchor points. We could get rid of this if we write a
+ ;; smarter `sqlind-ert-check-line-syntax'
+ (sqlind-ert-indent-buffer
+ (default-value 'sqlind-indentation-offsets-alist)
+ (default-value 'sqlind-basic-offset))
+ (goto-char (point-min))
+ (let ((syn (sqlind-syntax-of-line)))
+ (setq result (cons syn result)))
+ (while (= (forward-line 1) 0)
+ (let ((syn (sqlind-syntax-of-line)))
+ (setq result (cons syn result)))))
+ (reverse result)))
+
+(defun sqlind-collect-indentation-offsets-from-buffer (buffer rules basic-offset)
+ (let ((result '()))
+ (with-current-buffer buffer
+ (sqlind-ert-indent-buffer
+ (or rules (default-value 'sqlind-indentation-offsets-alist))
+ (or basic-offset (default-value 'sqlind-basic-offset)))
+ (goto-char (point-min))
+ (setq result (cons (current-indentation) result))
+ (while (= (forward-line 1) 0)
+ (setq result (cons (current-indentation) result))))
+ (reverse result)))
+
+\f
+;;......................................................... test helpers ....
+
+(defun sqlind-ert-indent-buffer (rules basic-offset)
+ "Indent the buffer according to RULES and BASIC-OFFSET.
+The RULES and BASIC-OFFSET are installed as
+`sqlind-indentation-offsets-alist' and `sqlind-basic-offset' than
+indent the whole buffer."
+ (when rules
+ (setq sqlind-indentation-offsets-alist rules))
+ (when basic-offset
+ (setq sqlind-basic-offset basic-offset))
+ ;; To ensure we are consistent in our offsets regardless of he users
+ ;; personal tab choices, setup spaces only indentation for this buffer.
+ (setq indent-tabs-mode nil)
+ (untabify (point-min) (point-max))
+ (indent-region (point-min) (point-max))
+ ;; (save-buffer) ; if you want to see the result of this command
+ (set-buffer-modified-p nil))
+
+(defun sqlind-ert-check-line-syntax (expected)
+ "Check that the current line has EXPECTED syntax.
+Get the syntax of the current line in the current buffer using
+`sqlind-syntax-of-line' and compare it against EXPECTED.
+
+ The comparison is done using the `should' ERT macro, so this
+function should be run a part of an ERT test."
+ (let ((actual (sqlind-syntax-of-line))
+ (info (format "%s:%s"
+ (buffer-file-name)
+ (line-number-at-pos))))
+ ;; NOTE: should does not appear to have a message argument, so the "cons"
+ ;; trick is used to add some information in case of failure.
+ (should
+ (equal (cons info actual) (cons info expected)))))
+
+(defun sqlind-ert-read-data (file)
+ "Read saved ELISP data from FILE."
+ (with-current-buffer (or (get-buffer file)
+ (find-file-noselect file))
+ (goto-char (point-min))
+ (read (current-buffer))))
+
+(defun sqlind-ert-check-file-syntax (sql-file data-file)
+ "Check the syntax of each line in SQL-FILE.
+The syntax of each line in SQL-FILE is checked against the
+previously saved syntax data in DATA-FILE. An error is signalled
+if there is a mismatch."
+ (let ((syntax-data (sqlind-ert-read-data data-file)))
+ (with-current-buffer (find-file sql-file)
+ (sqlind-minor-mode 1) ; ensure this is enabled
+ ;; NOTE: indent the buffer according to default rules first -- this
+ ;; affects anchor points.
+ ;; (message "sql-product: %s" sql-product)
+ (sqlind-ert-indent-buffer
+ (default-value 'sqlind-indentation-offsets-alist)
+ (default-value 'sqlind-basic-offset))
+ (goto-char (point-min))
+ (should (consp syntax-data)) ; "premature end of syntax-data"
+ (sqlind-ert-check-line-syntax (car syntax-data))
+ (setq syntax-data (cdr syntax-data))
+ (while (= (forward-line 1) 0)
+ (should (consp syntax-data)) ; "premature end of syntax-data"
+ (sqlind-ert-check-line-syntax (car syntax-data))
+ (setq syntax-data (cdr syntax-data))))))
+
+(defun sqlind-ert-check-line-indentation (expected)
+ "Check that the current line has EXPECTED indentation.
+The comparison is done using the `should' ERT macro, so this
+function should be run a part of an ERT test."
+ (let ((actual (current-indentation))
+ (info (format "%s:%s"
+ (buffer-file-name)
+ (line-number-at-pos))))
+ ;; NOTE: should does not appear to have a message argument, so the "cons"
+ ;; trick is used to add some information in case of failure.
+ (should
+ (equal (cons info actual) (cons info expected)))))
+
+(defun sqlind-ert-check-file-indentation (sql-file data-file rules basic-offset)
+ "Check that SQL-FILE is indented correctly according to RULES
+and BASIC-OFFSET The file is indented first according to RULES
+and BASIC-OFFSET, than each line is compared with the indentation
+information read from DATA-FILE (as generated by
+`sqlind-collect-indentation-offsets-from-buffer')"
+ (let ((indentation-data (sqlind-ert-read-data data-file)))
+ (with-current-buffer (find-file sql-file)
+ (sqlind-minor-mode 1)
+ ;; (message "sql-product: %s" sql-product)
+ (sqlind-ert-indent-buffer rules basic-offset)
+ (goto-char (point-min))
+ (should (consp indentation-data)) ; "premature end of indentation-data
+ (sqlind-ert-check-line-indentation (car indentation-data))
+ (setq indentation-data (cdr indentation-data))
+ (while (= (forward-line 1) 0)
+ (should (consp indentation-data)) ; "premature end of syntax-data"
+ (sqlind-ert-check-line-indentation (car indentation-data))
+ (setq indentation-data (cdr indentation-data))))))
+
+\f
+;;..................................................... the actual tests ....
+
+;; See https://gist.github.com/alex-hhh/834a91621680e826a27b2b08463eb12f
+
+(defvar m-indentation-offsets-alist
+ `((select-clause 0)
+ (insert-clause 0)
+ (delete-clause 0)
+ (update-clause 0)
+ (in-insert-clause +)
+ (in-select-clause sqlind-lineup-to-clause-end)
+ (nested-statement-continuation sqlind-lineup-into-nested-statement
+ sqlind-align-comma)
+ (nested-statement-close sqlind-lineup-to-anchor)
+ (select-column sqlind-indent-select-column
+ sqlind-align-comma)
+ (select-column-continuation sqlind-indent-select-column)
+ (select-table-continuation sqlind-indent-select-table
+ sqlind-lineup-joins-to-anchor
+ sqlind-lineup-open-paren-to-anchor
+ sqlind-align-comma)
+ ,@sqlind-default-indentation-offsets-alist))
+
+(ert-deftest sqlind-ert-pr17 ()
+ (sqlind-ert-check-file-syntax "test-data/pr17.sql" "test-data/pr17-syn.eld"))
+
+(ert-deftest sqlind-ert-pr17-indentation-default ()
+ (sqlind-ert-check-file-indentation
+ "test-data/pr17.sql" "test-data/pr17-io-default.eld"
+ (default-value 'sqlind-indentation-offsets-alist)
+ (default-value 'sqlind-basic-offset)))
+
+(ert-deftest sqlind-ert-pr17-indentation-left ()
+ (sqlind-ert-check-file-indentation
+ "test-data/pr17.sql" "test-data/pr17-io-left.eld"
+ sqlind-indentation-left-offsets-alist
+ (default-value 'sqlind-basic-offset)))
+
+(ert-deftest sqlind-ert-pr17-indentation-right ()
+ (sqlind-ert-check-file-indentation
+ "test-data/pr17.sql" "test-data/pr17-io-right.eld"
+ sqlind-indentation-right-offsets-alist
+ (default-value 'sqlind-basic-offset)))
+
+(ert-deftest sqlind-ert-pr7 ()
+ (sqlind-ert-check-file-syntax "test-data/pr7.sql" "test-data/pr7-syn.eld"))
+
+(ert-deftest sqlind-ert-case-stmt ()
+ (sqlind-ert-check-file-syntax "test-data/case-stmt.sql" "test-data/case-stmt-syn.eld"))
+
+(ert-deftest sqlind-ert-m-syn ()
+ (sqlind-ert-check-file-syntax "test-data/m.sql" "test-data/m-syn.eld"))
+
+(ert-deftest sqlind-ert-m-io ()
+ (sqlind-ert-check-file-indentation
+ "test-data/m.sql" "test-data/m-io.eld"
+ m-indentation-offsets-alist 4))
+
+(ert-deftest sqlind-ert-pr18 ()
+ (sqlind-ert-check-file-syntax "test-data/pr18.sql" "test-data/pr18-syn.eld"))
+
+(ert-deftest sqlind-ert-pr19 ()
+ (sqlind-ert-check-file-syntax "test-data/pr19.sql" "test-data/pr19-syn.eld"))
+
+(ert-deftest sqlind-ert-pr24 ()
+ (sqlind-ert-check-file-syntax "test-data/pr24.sql" "test-data/pr24-syn.eld"))
+
+(ert-deftest sqlind-ert-pr28 ()
+ (sqlind-ert-check-file-syntax "test-data/pr28.sql" "test-data/pr28-syn.eld"))
+
+(ert-deftest sqlind-ert-pr29 ()
+ (sqlind-ert-check-file-syntax "test-data/pr29.sql" "test-data/pr29-syn.eld"))
+
+(ert-deftest sqlind-ert-if-exists ()
+ (sqlind-ert-check-file-syntax "test-data/if-exists.sql" "test-data/if-exists-syn.eld"))
+
+(ert-deftest sqlind-ert-pr33 ()
+ (sqlind-ert-check-file-syntax "test-data/pr33.sql" "test-data/pr33-syn.eld"))
+
+(ert-deftest sqlind-ert-pr33-io-left ()
+ (sqlind-ert-check-file-indentation
+ "test-data/pr33.sql" "test-data/pr33-io-left.eld"
+ sqlind-indentation-left-offsets-alist 2))
+
+(ert-deftest sqlind-ert-pr36 ()
+ (sqlind-ert-check-file-syntax "test-data/pr36.sql" "test-data/pr36-syn.eld"))
+
+(ert-deftest sqlind-ert-pr36-io-left ()
+ (sqlind-ert-check-file-indentation "test-data/pr36.sql" "test-data/pr36-io-left.eld"
+ sqlind-indentation-left-offsets-alist 2))
+
+(ert-deftest sqlind-ert-pr37 ()
+ (sqlind-ert-check-file-syntax "test-data/pr37.sql" "test-data/pr37-syn.eld"))
+
+(ert-deftest sqlind-ert-pr37-io-left ()
+ (sqlind-ert-check-file-indentation "test-data/pr37.sql" "test-data/pr37-io-left.eld"
+ sqlind-indentation-left-offsets-alist 2))
+
+(ert-deftest sqlind-ert-pr39 ()
+ (sqlind-ert-check-file-syntax "test-data/pr39.sql" "test-data/pr39-syn.eld"))
+
+(ert-deftest sqlind-ert-pr40 ()
+ (sqlind-ert-check-file-syntax "test-data/pr40.sql" "test-data/pr40-syn.eld"))
+
+(ert-deftest sqlind-ert-pr42 ()
+ (sqlind-ert-check-file-syntax "test-data/pr42.sql" "test-data/pr42-syn.eld"))
+
+(ert-deftest sqlind-ert-pr46 ()
+ (sqlind-ert-check-file-syntax "test-data/pr46.sql" "test-data/pr46-syn.eld"))
+
+(ert-deftest sqlind-ert-pr48 ()
+ (sqlind-ert-check-file-syntax "test-data/pr48.sql" "test-data/pr48-syn.eld"))
+
+(ert-deftest sqlind-ert-pr49 ()
+ (sqlind-ert-check-file-syntax "test-data/pr49.sql" "test-data/pr49-syn.eld"))
+
+(ert-deftest sqlind-ert-pr50-io-left ()
+ (sqlind-ert-check-file-indentation "test-data/pr50.sql" "test-data/pr50-io-left.eld"
+ sqlind-indentation-left-offsets-alist 2))
+
+(ert-deftest sqlind-ert-pr52-io-left ()
+ (sqlind-ert-check-file-indentation "test-data/pr52.sql" "test-data/pr52-io-left.eld"
+ sqlind-indentation-left-offsets-alist 2))
+
+(ert-deftest sqlind-ert-pr53-io-left ()
+ (sqlind-ert-check-file-indentation "test-data/pr53.sql" "test-data/pr53-io-left.eld"
+ sqlind-indentation-left-offsets-alist 2))
+
+(ert-deftest sqlind-ert-pr54 ()
+ (sqlind-ert-check-file-syntax "test-data/pr54.sql" "test-data/pr54-syn.eld"))
+
+(ert-deftest sqlind-ert-pr60 ()
+ (sqlind-ert-check-file-syntax "test-data/pr60.sql" "test-data/pr60-syn.eld"))
+
+(ert-deftest sqlind-ert-pr64 ()
+ (sqlind-ert-check-file-syntax "test-data/pr64.sql" "test-data/pr64-syn.eld"))
+
+(ert-deftest sqlind-ert-pr66 ()
+ (sqlind-ert-check-file-syntax "test-data/pr66.sql" "test-data/pr66-syn.eld"))
+
+(ert-deftest sqlind-ert-pr67 ()
+ (sqlind-ert-check-file-syntax "test-data/pr67.sql" "test-data/pr67-syn.eld"))
+
+(ert-deftest sqlind-ert-pr68 ()
+ (sqlind-ert-check-file-syntax "test-data/pr68.sql" "test-data/pr68-syn.eld"))
+
+(ert-deftest sqlind-ert-pr70 ()
+ (sqlind-ert-check-file-syntax "test-data/pr70.sql" "test-data/pr70-syn.eld"))
+
+(ert-deftest sqlind-ert-pr73 ()
+ (sqlind-ert-check-file-syntax "test-data/pr73.sql" "test-data/pr73-syn.eld"))
+
+(ert-deftest sqlind-ert-pr75-postgres ()
+ (sqlind-ert-check-file-syntax
+ "test-data/pr75-postgres.sql"
+ "test-data/pr75-postgres-syn.eld"))
+
+(ert-deftest sqlind-ert-pr75-oracle ()
+ (sqlind-ert-check-file-syntax
+ "test-data/pr75-oracle.sql"
+ "test-data/pr75-oracle-syn.eld"))
+
+(ert-deftest sqlind-ert-pr80 ()
+ (sqlind-ert-check-file-syntax
+ "test-data/pr80.sql"
+ "test-data/pr80-syn.eld"))
+
+(ert-deftest sqlind-ert-pr81 ()
+ (sqlind-ert-check-file-syntax
+ "test-data/pr81.sql"
+ "test-data/pr81-syn.eld"))
+
+(ert-deftest sqlind-ert-pr83 ()
+ (sqlind-ert-check-file-syntax
+ "test-data/pr83.sql"
+ "test-data/pr83-syn.eld"))
+
+(ert-deftest sqlind-ert-pr84 ()
+ (sqlind-ert-check-file-syntax
+ "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
--- /dev/null
+;;; sql-indent.el --- Support for indenting code in SQL files. -*- lexical-binding: t -*-
+
+;; Copyright (C) 2015, 2017-2018 Free Software Foundation, Inc
+
+;; Author: Alex Harsanyi <AlexHarsanyiNOSPAMgmail.com>
+;; Created: 27 Sep 2006
+;; Version: 1.4
+;; Keywords: languages sql
+;; Homepage: https://github.com/alex-hhh/emacs-sql-indent
+;; Package-Requires: ((cl-lib "0.5"))
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3 of the License, or
+;; (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; `sqlind-minor-mode' is a minor mode that enables syntax-based indentation
+;; for `sql-mode' buffers: the TAB key indents the current line based on the
+;; SQL code on previous lines. To setup syntax-based indentation for every
+;; SQL buffer, add `sqlind-minor-mode' to `sql-mode-hook'. Indentation rules
+;; are flexible and can be customized to match your personal coding style.
+;; For more information, see the "sql-indent.org" file.
+;;
+;; The package also defines align rules so that the `align' function works for
+;; SQL statements, see `sqlind-align-rules'.
+
+;;; Code:
+
+(require 'sql)
+(require 'cl-lib)
+
+;;;; General setup
+
+(defvar sqlind-syntax-table
+ (let ((table (make-syntax-table)))
+ ;; C-style comments /**/ (see elisp manual "Syntax Flags"))
+ (modify-syntax-entry ?/ ". 14" table)
+ (modify-syntax-entry ?* ". 23" table)
+ ;; double-dash starts comment
+ (modify-syntax-entry ?- ". 12b" table)
+ ;; newline and formfeed end coments
+ (modify-syntax-entry ?\n "> b" table)
+ (modify-syntax-entry ?\f "> b" table)
+ ;; single quotes (') quotes delimit strings
+ (modify-syntax-entry ?' "\"" table)
+ ;; backslash is no escape character
+ (modify-syntax-entry ?\\ "." table)
+
+ ;; the following are symbol constituents. Note that the dot '.' is more
+ ;; usefull as a symbol constituent than as a punctuation char.
+
+ (modify-syntax-entry ?_ "_" table)
+ (modify-syntax-entry ?. "_" table)
+ (modify-syntax-entry ?$ "_" table)
+ (modify-syntax-entry ?# "_" table)
+ (modify-syntax-entry ?% "_" table)
+
+ table)
+ "Syntax table used in `sql-mode' for indenting SQL code.
+This is slightly different than the syntax table used for
+navigation: some punctuation characters are made symbol
+constituents so that syntactic navigation works over them.")
+
+;;;; Utilities
+
+;; The following routines perform rudimentary syntactical analysis of SQL
+;; code. The indentation engine decides how to indent based on what this code
+;; returns. The main function is `sqlind-syntax-of-line'.
+;;
+;; To examine the syntax of the current line, you can use the
+;; `sqlind-show-syntax-of-line'. This is only useful if you want to debug this
+;; package or are just curious.
+
+(defconst sqlind-comment-start-skip "\\(--+\\|/\\*+\\)\\s *"
+ "Regexp to match the start of a SQL comment.")
+
+(defconst sqlind-comment-end "\\*+/"
+ "Regexp to match the end of a multiline SQL comment.")
+
+(defvar sqlind-comment-prefix "\\*+\\s "
+ "Regexp to match the line prefix inside comments.
+This is used to indent multi-line comments.")
+
+(defsubst sqlind-in-comment-or-string (pos)
+ "Return non nil if POS is inside a comment or a string.
+We actually return 'string if POS is inside a string, 'comment if
+POS is inside a comment, nil otherwise."
+ (syntax-ppss-context (syntax-ppss pos)))
+
+(defun sqlind-backward-syntactic-ws ()
+ "Move point backwards over whitespace and comments.
+Leave point on the first character which is not syntactic
+whitespace, or at the beginning of the buffer."
+ (let ((done nil))
+ (while (not done)
+ (skip-chars-backward " \t\n\r\f\v")
+ (unless (eq (point) (point-min))
+ (forward-char -1))
+ (let ((pps (syntax-ppss (point))))
+ (if (nth 4 pps) ; inside a comment?
+ (goto-char (nth 8 pps)) ; move to comment start than repeat
+ (setq done t)))))
+ (point))
+
+(defun sqlind-forward-syntactic-ws ()
+ "Move point forward over whitespace and comments.
+Leave point at the first character which is not syntactic
+whitespace, or at the end of the buffer."
+ ;; if we are inside a comment, move to the comment start and scan
+ ;; from there.
+ (let ((pps (syntax-ppss (point))))
+ (when (nth 4 pps)
+ (goto-char (nth 8 pps))))
+ (let ((done nil))
+ (while (not done)
+ (skip-chars-forward " \t\n\r\f\v")
+ (cond ((looking-at sqlind-comment-start-skip) (forward-comment 1))
+ ;; a slash ("/") by itself is a SQL*plus directive and
+ ;; counts as whitespace
+ ((looking-at "/\\s *$") (goto-char (match-end 0)))
+ (t (setq done t)))))
+ (point))
+
+(defun sqlind-search-backward (start regexp limit)
+ "Search for REGEXP from START backward until LIMIT.
+Finds a match that is not inside a comment or string, moves point
+to the match and returns it. If no match is found, point is moved
+to LIMIT and nil is returned."
+ (goto-char start)
+ (let ((done nil))
+ (while (and (not done)
+ (re-search-backward regexp limit 'noerror))
+ (when (sqlind-same-level-statement (point) start)
+ (setq done (point))))
+ done))
+
+(defsubst sqlind-match-string (pos)
+ "Return the match data at POS in the current buffer.
+This is similar to `match-data', but the text is fetched without
+text properties and it is conveted to lower case."
+ (let ((start (match-beginning pos))
+ (end (match-end pos)))
+ (when (and start end)
+ (downcase (buffer-substring-no-properties start end)))))
+
+(defsubst sqlind-labels-match (lb1 lb2)
+ "Return t when LB1 equals LB2 or LB1 is an empty string.
+This is used to compare start/end block labels where the end
+block label might be empty."
+ (or (string= lb1 lb2)
+ (string= lb1 "")))
+
+(defun sqlind-same-level-statement (point start)
+ "Return t if POINT is at the same syntactic level as START.
+This means that POINT is at the same nesting level and not inside
+a string or comment."
+ (save-excursion
+ (let ((ppss-point (syntax-ppss point))
+ (ppss-start (syntax-ppss start)))
+ (and (equal (nth 3 ppss-point) (nth 3 ppss-start)) ; string
+ (equal (nth 4 ppss-point) (nth 4 ppss-start)) ; comment
+ (= (nth 0 ppss-point) (nth 0 ppss-start)))))) ; same nesting
+
+(defun sqlind-column-definition-start (pos limit)
+ "Find the beginning of a column definition in a select statement.
+POS is the current position of the line to be indented, assumed
+to be in a 'select-column-continuation syntax.
+
+LIMIT is the limit of the search, the beginning of the select
+statement."
+ (save-excursion
+ (goto-char pos)
+ (catch 'found
+ (while (re-search-backward "," limit 'noerror)
+ (when (sqlind-same-level-statement (point) limit)
+ (forward-char 1)
+ (sqlind-forward-syntactic-ws)
+ (throw 'found (point))))
+ ;; nothing was found in (while ...) so try to find the first column definition.
+ (goto-char limit)
+ (forward-sexp)
+ (sqlind-forward-syntactic-ws)
+ (point))))
+
+(defun sqlind-syntax (context)
+ "Return the most specific syntax of CONTEXT.
+See `sqlind-syntax-of-line' for the definition of CONTEXT."
+ (when context
+ (caar context)))
+
+(defun sqlind-syntax-symbol (context)
+ "Return the syntax symbol for the most specific syntax of CONTEXT.
+See `sqlind-syntax-of-line' for the definition of CONTEXT."
+ (when context
+ (let ((syntax-part (caar context)))
+ (if (symbolp syntax-part)
+ syntax-part
+ (car syntax-part)))))
+
+(defun sqlind-syntax-keyword (context)
+ "Return the syntax keyword for the most specific syntax of CONTEXT.
+This is used for complex syntax symbols like '(in-block case
+\"\"), in that case, it will return the 'case symbol. See
+`sqlind-syntax-of-line' for the definition of CONTEXT."
+ (when context
+ (let ((syntax-part (caar context)))
+ (if (symbolp syntax-part)
+ nil ; no KEYWORD
+ (nth 1 syntax-part)))))
+
+(defun sqlind-anchor-point (context)
+ "Return the anchor point for the most specifc syntax of CONTEXT.
+See `sqlind-syntax-of-line' for the definition of CONTEXT."
+ (when context
+ (cdar context)))
+
+(defun sqlind-outer-context (context)
+ "Return the outer context from CONTEXT.
+See `sqlind-syntax-of-line' for the definition of CONTEXT."
+ (when context
+ (cdr context)))
+
+(defun sqlind-find-context (syntax-symbol context)
+ "Find SYNTAX-SYMBOL in the CONTEXT chain.
+CONTEXT chain is a list of (SYNTAX-SYMBOL . ANCHOR), as returned
+by `sqlind-syntax-of-line'. The function finds the fist element
+which matches the specified syntax symbol and returns the list
+from that point.
+
+See `sqlind-indentation-syntax-symbols' for the possible syntax
+symbols and their meaning."
+ (cond ((null context)
+ nil)
+ ((eq syntax-symbol (sqlind-syntax-symbol context))
+ context)
+ (t
+ (sqlind-find-context syntax-symbol (sqlind-outer-context context)))))
+
+(defun sqlind-looking-at-begin-transaction ()
+ "Return t if the point is on a \"begin transaction\" statement."
+ (and (looking-at "begin")
+ (save-excursion
+ (forward-word 1)
+ (sqlind-forward-syntactic-ws)
+ (looking-at "transaction\\>\\|work\\>\\|;"))))
+
+;;;; Syntactic analysis of SQL code
+
+;;;;; Find the beginning of the current statement
+
+(defconst sqlind-sqlplus-directive
+ (concat ;; SET is handled in a special way, to avoid conflicts with the SET
+ ;; keyworkd in updated clauses
+ "\\(^set\\s-+\\w+\\(\\s-+[^=].*\\)?$\\)\\|\\(^"
+ (regexp-opt '("column" "rem" "define" "spool" "prompt"
+ "clear" "compute" "whenever" "@" "@@" "start")
+ t)
+ "\\)\\b")
+ "Match an SQL*Plus directive at the beginning of a line.
+A directive always stands on a line by itself -- we use that to
+determine the statement start in SQL*Plus scripts.")
+
+(defconst sqlind-sqlite-directive
+ "^[.].*?\\>"
+ "Match an SQLite directive at the beginning of a line.
+A directive always stands on a line by itself -- we use that to
+determine the statement start in SQLite scripts.")
+
+(defconst sqlind-ms-directive
+ (concat
+ "\\(^set\\s-+\\(\\w\\|\\s_\\)+\\s-+\\(on\\|off\\)\\)\\|\\(^"
+ (regexp-opt '("use" "go" "declare") t)
+ "\\)\\b")
+ "Match an MS SQL Sever directive at the beginning of a line.")
+
+(defun sqlind-beginning-of-directive ()
+ "Return the position of an SQL directive, or nil.
+We will never move past one of these in our scan. We also assume
+they are one-line only directives."
+ (let ((rx (cl-case (and (boundp 'sql-product) sql-product)
+ (ms sqlind-ms-directive)
+ (sqlite sqlind-sqlite-directive)
+ (oracle sqlind-sqlplus-directive)
+ (t nil))))
+ (when rx
+ (save-excursion
+ (when (re-search-backward rx nil 'noerror)
+ (forward-line 1)
+ (point))))))
+
+(defvar sqlind-search-limit nil
+ "Limit of search when looking for syntactic elements.
+This variable is dynamically bound.")
+
+(defun sqlind-beginning-of-statement-1 (limit)
+ "Return the position of a block start, or nil.
+But don't go before LIMIT."
+ (save-excursion
+ (catch 'done
+ (while (> (point) (or limit (point-min)))
+ (when (re-search-backward
+ ";\\|:=\\|\\_<\\(declare\\|begin\\|cursor\\|for\\|while\\|loop\\|if\\|then\\|else\\|elsif\\|elseif\\)\\_>\\|)"
+ limit 'noerror)
+ (unless (sqlind-in-comment-or-string (point))
+ (let ((candidate-pos (match-end 0)))
+ (cond ((looking-at ")")
+ ;; Skip parenthesis expressions, we don't want to find one
+ ;; of the keywords inside one of them and think this is a
+ ;; statement start.
+ (progn (forward-char 1) (forward-sexp -1)))
+ ((looking-at "cursor\\|for\\|while")
+ ;; statement begins at the start of the keyword
+ (throw 'done (point)))
+ ((looking-at "else?if")
+ ;; statement begins at the start of the keyword
+ (throw 'done (point)))
+ ((looking-at "then\\|else")
+ ;; then and else start statements when they are inside
+ ;; blocks, not expressions.
+ (sqlind-backward-syntactic-ws)
+ (when (looking-at ";")
+ ;; Statement begins after the keyword
+ (throw 'done candidate-pos)))
+ ((looking-at "if")
+ (when (sqlind-good-if-candidate)
+ ;; statement begins at the start of the keyword
+ (throw 'done (point))))
+ ((looking-at ":=")
+ ;; assignment statements start at the assigned variable
+ (sqlind-backward-syntactic-ws)
+ (forward-sexp -1)
+ (throw 'done (point)))
+ ((sqlind-looking-at-begin-transaction)
+ ;; This is a "begin transaction" call, statement begins
+ ;; at "begin", see #66
+ (throw 'done (point)))
+ ((not (sqlind-in-comment-or-string (point)))
+ (throw 'done candidate-pos))))))))))
+
+(defun sqlind-beginning-of-statement ()
+ "Move point to the beginning of the current statement."
+ (interactive)
+
+ (goto-char
+ (or
+ ;; If we are inside a paranthesis expression, the start is the start of
+ ;; that expression.
+ (let ((ppss (syntax-ppss (point))))
+ (when (> (nth 0 ppss) 0)
+ (nth 1 ppss)))
+ ;; Look for an ordinary statement start
+ (or (sqlind-beginning-of-statement-1 sqlind-search-limit)
+ ;; Fall back on the search limit
+ sqlind-search-limit)
+ ;; ... or point-min
+ (point-min)))
+
+ ;; a loop keyword might be part of an "end loop" statement, in that case,
+ ;; the statement starts with the "end" keyword. we use skip-syntax-backward
+ ;; so we won't skip over a semicolon (;)
+ (let ((pos (point)))
+ (skip-syntax-backward "w")
+ (if (looking-at "loop")
+ (progn
+ (forward-word -1)
+ (if (looking-at "\\bend\\b")
+ (goto-char (match-beginning 0))
+ (goto-char pos)))
+ (goto-char pos)))
+
+ ;; now skip over any whitespace and comments until we find the first
+ ;; character that is program code.
+ (sqlind-forward-syntactic-ws))
+
+;;;;; Find the syntax and beginning of the current block
+
+(defconst sqlind-end-statement-regexp
+ "\\_<end\\_>\\(?:[ \t\n\r\f]*\\)\\(if\\_>\\|loop\\_>\\|case\\_>\\)?\\(?:[ \t\n\r\f]*\\)\\([a-z0-9_]+\\)?"
+ "Match an end of statement.
+Matches a string like \"end if|loop|case MAYBE-LABEL\".")
+
+(defvar sqlind-end-stmt-stack nil
+ "Stack of end-of-statement positions.
+This is used by the sqlind-maybe-* functions to skip over SQL
+programming blocks. This variable is dynamically bound.")
+
+(defun sqlind-maybe-end-statement ()
+ "Look for SQL end statements return t if found one.
+If (point) is at an end statement, add it to the
+`sqlind-end-stmt-stack' and return t, otherwise return nil.
+
+See also `sqlind-beginning-of-block'"
+ (when (looking-at sqlind-end-statement-regexp)
+ (prog1 t ; make sure we return t
+ (let ((kind (or (sqlind-match-string 1) ""))
+ (label (or (sqlind-match-string 2) "")))
+ (push (list (point) (if (equal kind "") nil (intern kind)) label)
+ sqlind-end-stmt-stack)))))
+
+(defun sqlind-maybe-then-statement ()
+ "Find the corresponding start block for a \"then\" statement.
+When (point) is on a \"then\" statement, find the corresponding
+start of the block and report its syntax. This code will skip
+over nested statements to determine the actual start.
+
+Only keywords in program code are matched, not the ones inside
+expressions.
+
+See also `sqlind-beginning-of-block'"
+ (when (looking-at "then")
+ (prog1 t ; make sure we return t
+ (let ((start-pos (point)))
+
+ ;; a then keyword at the begining of a line is a block start
+ (when (null sqlind-end-stmt-stack)
+ (save-excursion
+ (back-to-indentation)
+ (when (eq (point) start-pos)
+ (throw 'finished (list 'in-block 'then "")))))
+
+ ;; if it is not at the beginning of a line, the beginning of
+ ;; the statement is the block start
+ (ignore-errors (forward-char -1))
+ (sqlind-beginning-of-statement)
+ ;; a then keyword only starts a block when it is part of an
+ ;; if, case/when or exception statement
+ (cond
+ ((looking-at "\\(<<[a-z0-9_]+>>\\)?\\(?:[ \t\n\r\f]*\\)\\(\\(?:els\\)?if\\)\\_>")
+ (let ((if-label (sqlind-match-string 1))
+ (if-kind (intern (sqlind-match-string 2)))) ; can be if or elsif
+ (setq if-label (if if-label (substring if-label 2 -2) ""))
+ (if (null sqlind-end-stmt-stack)
+ (throw 'finished (list 'in-block if-kind if-label))
+ ;; "if" blocks (but not "elsif" blocks) need to be
+ ;; ended with "end if"
+ (when (eq if-kind 'if)
+ (cl-destructuring-bind (pos kind label)
+ (pop sqlind-end-stmt-stack)
+ (unless (and (eq kind 'if)
+ (sqlind-labels-match label if-label))
+ (throw 'finished
+ (list 'syntax-error
+ "bad closing for if block" (point) pos))))))))
+ ((looking-at "\\(<<[a-z0-9_]+>>\\)?\\(?:[ \t\n\r\f]*\\)case\\_>")
+ ;; find the nearest when block, but only if there are no
+ ;; end statements in the stack
+ (let ((case-label (sqlind-match-string 1)))
+ (setq case-label
+ (if case-label (substring case-label 2 -2) ""))
+ (if (null sqlind-end-stmt-stack)
+ (save-excursion
+ (when (sqlind-search-backward start-pos "\\_<when\\_>" (point))
+ (throw 'finished (list 'in-block 'case case-label))))
+ ;; else
+ (cl-destructuring-bind (pos kind label)
+ (pop sqlind-end-stmt-stack)
+ (unless (and (eq kind 'case)
+ (sqlind-labels-match label case-label))
+ (throw 'finished
+ (list 'syntax-error
+ "bad closing for case block" (point) pos)))))))
+ ((looking-at "exception\\_>")
+ ;; an exception statement is a block start only if we have
+ ;; no end statements in the stack
+ (when (null sqlind-end-stmt-stack)
+ (throw 'finished (list 'in-block 'exception ""))))
+ (t ; restore position if we didn't do anything
+ (goto-char start-pos)
+ nil))))))
+
+(defun sqlind-good-if-candidate ()
+ "Return non-nil if point is on an actual if statement.
+We try to avoid false positives, like \"end if\" or the various
+\"drop STUFF if exists\" variants."
+ (and (looking-at "if")
+ (save-excursion
+ (sqlind-backward-syntactic-ws)
+ (forward-word -1)
+ ;; we don't want to match an "end if", and things like "drop index if
+ ;; exists..." and "create index if not exist..."
+ (not (looking-at "end\\|table\\|view\\|index\\|trigger\\|procedure\\|function\\|package\\|body")))))
+
+(defun sqlind-maybe-if-statement ()
+ "If (point) is on an IF statement, report its syntax."
+ (when (sqlind-good-if-candidate)
+ (cond ((null sqlind-end-stmt-stack)
+ (throw 'finished (list 'in-block 'if "")))
+ (t
+ (cl-destructuring-bind (pos kind _label)
+ (pop sqlind-end-stmt-stack)
+ (unless (eq kind 'if)
+ (throw 'finshed
+ (list 'syntax-error
+ "bad closing for if block" (point) pos))))))))
+
+(defun sqlind-maybe-case-statement ()
+ "If (point) is on a case statement."
+ (when (looking-at "case")
+ (save-excursion
+ (sqlind-backward-syntactic-ws)
+ (forward-word -1)
+ (unless (looking-at "end") ; we don't want to match an "end case" here
+ (if (null sqlind-end-stmt-stack)
+ (throw 'finished (list 'in-block 'case ""))
+ (cl-destructuring-bind (pos kind _label)
+ (pop sqlind-end-stmt-stack)
+ (unless (or (not kind) (eq kind 'case))
+ (throw 'finished
+ (list 'syntax-error
+ "bad closing for case block" (point) pos)))))))))
+
+(defun sqlind-maybe-else-statement ()
+ "If (point) is on an ELSE statement, report its syntax.
+Only keywords in program code are matched, not the ones inside
+expressions.
+
+See also `sqlind-beginning-of-block'"
+ ;; an else statement is only a block start if the `sqlind-end-stmt-stack' is
+ ;; empty, otherwise, we don't do anything.
+ (when (and (looking-at "els\\(e\\|if\\)")
+ (null sqlind-end-stmt-stack))
+ (throw 'finished (list 'in-block (intern (sqlind-match-string 0)) ""))))
+
+(defun sqlind-maybe-loop-statement ()
+ "If (point) is on a LOOP statement, report its syntax.
+Only keywords in program code are matched, not the ones inside
+expressions.
+
+See also `sqlind-beginning-of-block'"
+ (when (looking-at "loop")
+ (prog1 t ; make sure we return t
+ (sqlind-beginning-of-statement)
+ ;; note that we might have found a loop in an "end loop;" statement.
+ (or (sqlind-maybe-end-statement)
+ (progn
+ (let ((posn (point)))
+ (unless (looking-at "<<") ;; whe're inside an label.
+ (forward-word -1)
+ (back-to-indentation))
+ (let ((loop-label (if (looking-at "<<\\([a-z0-9_]+\\)>>")
+ (sqlind-match-string 1) "")))
+ (goto-char posn)
+ ;; start of loop. this always starts a block, we only check if
+ ;; the labels match
+ (if (null sqlind-end-stmt-stack)
+ (throw 'finished (list 'in-block 'loop loop-label))
+ (cl-destructuring-bind (pos kind label)
+ (pop sqlind-end-stmt-stack)
+ (unless (and (eq kind 'loop)
+ (sqlind-labels-match label loop-label))
+ (throw 'finished
+ (list 'syntax-error
+ "bad closing for loop block" (point) pos))))))))))))
+
+(defun sqlind-maybe-begin-statement ()
+ "Return the syntax of a \"begin\" statement.
+If (point) is on a \"begin\" statement, report its syntax. Only
+keywords in program code are matched, not the ones inside
+expressions.
+
+See also `sqlind-beginning-of-block'"
+ (when (and (looking-at "begin") (not (sqlind-looking-at-begin-transaction)))
+ ;; a begin statement starts a block unless it is the first begin in a
+ ;; procedure over which we need to skip it.
+ (prog1 t ; make sure we return t
+ (let* ((saved-pos (point))
+ (begin-label (save-excursion
+ (sqlind-beginning-of-statement)
+ (if (looking-at "<<\\([a-z0-9_]+\\)>>\\_>")
+ (sqlind-match-string 1) "")))
+ (previous-block (save-excursion
+ (ignore-errors (forward-char -1))
+ (cons (sqlind-beginning-of-block) (point))))
+ (previous-block-kind (nth 0 previous-block)))
+
+ (goto-char saved-pos)
+ (when (null sqlind-end-stmt-stack)
+ (throw 'finished
+ (cond ((memq previous-block-kind '(toplevel declare-statement))
+ (list 'in-begin-block 'toplevel-block begin-label))
+ ((and (listp previous-block-kind)
+ (eq (nth 0 previous-block-kind) 'defun-start))
+ (list 'in-begin-block 'defun (nth 1 previous-block-kind)))
+ ((and (listp previous-block-kind)
+ (memq (nth 0 previous-block-kind) '(package package-body)))
+ (list 'in-begin-block 'package (nth 1 previous-block-kind)))
+ (t
+ (list 'in-begin-block nil begin-label)))))
+
+ (cl-destructuring-bind (pos kind label) (pop sqlind-end-stmt-stack)
+ (cond
+ (kind
+ (throw 'finished
+ (list 'syntax-error "bad closing for begin block" (point) pos)))
+
+ ((not (equal begin-label ""))
+ ;; we have a begin label. In this case it must match the end
+ ;; label
+ (unless (equal begin-label label)
+ (throw 'finished
+ (list 'syntax-error "mismatched block labels" (point) pos))))
+
+ (t
+ ;; we don't have a begin label. In this case, the begin
+ ;; statement might not start a block if it is the begining of a
+ ;; procedure or declare block over which we need to skip.
+
+ (cond ((memq previous-block-kind '(toplevel declare-statement))
+ (goto-char (cdr previous-block)))
+ ((and (listp previous-block-kind)
+ (memq (nth 0 previous-block-kind)
+ '(defun-start package package-body)))
+ (unless (sqlind-labels-match
+ label (nth 1 previous-block-kind))
+ (throw 'finished
+ (list 'syntax-error
+ "bad end label for defun" (point) pos)))
+ (goto-char (cdr previous-block)))))))))))
+
+(defun sqlind-maybe-declare-statement ()
+ "If (point) is on a DECLARE statement, report its syntax.
+Only keywords in program code are matched, not the ones inside
+expressions, also we don't match DECLARE directives here.
+
+See also `sqlind-beginning-of-block'"
+ (when (looking-at "declare")
+ ;; a declare block is always toplevel, if it is not, its an error
+ (throw 'finished
+ (if (null sqlind-end-stmt-stack)
+ 'declare-statement
+ (list 'syntax-error "nested declare block" (point) (point))))))
+
+(defun sqlind-maybe-skip-create-options ()
+ "Move point past any MySQL option declarations.
+
+Statements like \"CREATE VIEW\" or \"CREATE TABLE\" can have
+various options betwen the CREATE keyword and the thing being
+created. If such options exist at (point) the cursor is moved
+past them.
+
+Currently we move over the following options, for different
+products:
+
+MySQL:
+
+ TEMPORARY
+ ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}
+ DEFINER = { user | CURENT_USER }
+ SQL SECURITY { DEFINER | INVOKER }
+
+PostgresSQL
+
+ TEMP
+ TEMPORARY
+ GLOBAL
+ LOCAL
+ UNLOGGED
+ MATERIALIZED
+
+Oracle
+
+ PRIVATE
+ TEMP
+ TEMPORARY
+ MATERIALIZED
+
+We don't consider if the options are valid or not for the thing
+being created. We just skip any and all of them that are
+present."
+ (cond
+ ((eq sql-product 'mysql)
+ (catch 'finished
+ (while t
+ (cond
+ ((looking-at "temporary\\_>")
+ (goto-char (match-end 0))
+ (sqlind-forward-syntactic-ws))
+ ((looking-at "\\(definer\\|algorithm\\)\\(\\s-\\|[\n]\\)*=\\(\\s-\\|[\n]\\)*\\S-+")
+ (goto-char (match-end 0))
+ (sqlind-forward-syntactic-ws))
+ ((looking-at "sql\\(\\s-\\|[\n]\\)+security\\(\\s-\\|[\n]\\)+\\S-+")
+ (goto-char (match-end 0))
+ (sqlind-forward-syntactic-ws))
+ (t (throw 'finished nil))))))
+ ((eq sql-product 'postgres)
+ (catch 'finished
+ (while t
+ (cond
+ ((looking-at "temp\\(orary\\)?\\_>")
+ (goto-char (match-end 0))
+ (sqlind-forward-syntactic-ws))
+ ((looking-at "\\(global\\|local\\|unlogged\\)\\_>")
+ (goto-char (match-end 0))
+ (sqlind-forward-syntactic-ws))
+ ((looking-at "materialized\\_>")
+ (goto-char (match-end 0))
+ (sqlind-forward-syntactic-ws))
+ (t (throw 'finished nil))))))
+ ((eq sql-product 'oracle)
+ (catch 'finished
+ (while t
+ (cond
+ ((looking-at "temp\\(orary\\)\\_>")
+ (goto-char (match-end 0))
+ (sqlind-forward-syntactic-ws))
+ ((looking-at "materialized\\_>")
+ (goto-char (match-end 0))
+ (sqlind-forward-syntactic-ws))
+ ((looking-at "private\\_>")
+ (goto-char (match-end 0))
+ (sqlind-forward-syntactic-ws))
+ (t (throw 'finished nil))))))))
+
+(defun sqlind-maybe-create-statement ()
+ "If (point) is on a CREATE statement, report its syntax.
+See also `sqlind-beginning-of-block'"
+ (when (or (looking-at "create\\_>\\(?:[ \t\n\r\f]+\\)\\(or\\(?:[ \t\n\r\f]+\\)replace\\_>\\)?")
+ (looking-at "alter\\_>"))
+ (prog1 t ; make sure we return t
+ (save-excursion
+ ;; let's see what are we creating
+ (goto-char (match-end 0))
+ (sqlind-forward-syntactic-ws)
+ (sqlind-maybe-skip-create-options)
+ (let ((what (intern (downcase (buffer-substring-no-properties
+ (point)
+ (progn (forward-word) (point))))))
+ (name (downcase (buffer-substring-no-properties
+ (progn (sqlind-forward-syntactic-ws)
+ ;; Skip over a possible "if (not)
+ ;; exists", to get the actual name
+ (when (looking-at "if\\(\\s-\\|[\n]\\)+\\(not\\)?\\(\\s-\\|[\n]\\)exists")
+ (goto-char (match-end 0))
+ (sqlind-forward-syntactic-ws))
+ (point))
+ (progn (skip-syntax-forward "w_()") (point))))))
+ (when (and (eq what 'package) (equal name "body"))
+ (setq what 'package-body)
+ (setq name (downcase
+ (buffer-substring-no-properties
+ (progn (sqlind-forward-syntactic-ws) (point))
+ (progn (skip-syntax-forward "w_()") (point))))))
+
+ ;; Keep just the name, not the argument list
+ (when (string-match "\\(.*?\\)(" name)
+ (setq name (match-string 1 name)))
+
+ (if (memq what '(procedure function package package-body))
+ ;; check is name is in the form user.name, if so then suppress user part.
+ (progn
+ (when (string-match "\\(?:.*\\.\\)?\\(.*\\)" name)
+ (setq name (match-string 1 name)))
+ (if (null sqlind-end-stmt-stack)
+ (throw 'finished
+ (list (if (memq what '(procedure function)) 'defun-start what)
+ name))
+ (cl-destructuring-bind (pos kind label) (pop sqlind-end-stmt-stack)
+ (when (not (eq kind nil))
+ (throw 'finished
+ (list 'syntax-error
+ "bad closing for create block" (point) pos)))
+ (unless (sqlind-labels-match label name)
+ (throw 'finished
+ (list 'syntax-error
+ "label mismatch in create block" (point) pos))))))
+ ;; we are creating a non-code block thing: table, view,
+ ;; index, etc. These things only exist at toplevel.
+ (unless (null sqlind-end-stmt-stack)
+ (throw 'finished
+ (list 'syntax-error "nested create statement" (point) (point))))
+ (throw 'finished (list 'create-statement what name))))))))
+
+(defun sqlind-maybe-defun-statement ()
+ "If (point) is on a procedure definition statement, report its syntax.
+See also `sqlind-beginning-of-block'"
+ (catch 'exit
+ (when (looking-at "\\(procedure\\|function\\)\\(?:[ \t\n\r\f]+\\)\\([a-z0-9_]+\\)")
+ (prog1 t ; make sure we return t
+ (let ((proc-name (sqlind-match-string 2)))
+ ;; need to find out if this is a procedure/function
+ ;; declaration or a definition
+ (save-excursion
+ (goto-char (match-end 0))
+ (sqlind-forward-syntactic-ws)
+ ;; skip param list, if any.
+ (when (looking-at "(")
+ (ignore-errors (forward-sexp 1))
+ (sqlind-forward-syntactic-ws))
+ (when (looking-at "return\\(?:[ \t\n\r\f]+\\)\\([a-z0-9_.]+\\(?:%\\(?:row\\)?type\\)?\\)")
+ (goto-char (match-end 0))
+ (sqlind-forward-syntactic-ws))
+ (when (looking-at ";")
+ ;; not a procedure after all.
+ (throw 'exit nil)))
+
+ ;; Find out if it is a drop procedure or function statement
+ (save-excursion
+ (sqlind-backward-syntactic-ws)
+ (forward-word -1)
+ (when (looking-at "drop")
+ ;; not a procedure after all
+ (throw 'exit nil)))
+
+ ;; so it is a definition
+
+ ;; if the procedure starts with "create or replace", move
+ ;; point to the real start
+ (let ((real-start (point)))
+ (save-excursion
+ (forward-word -1)
+ (when (looking-at "replace")
+ (forward-word -2))
+ (when (and (or (looking-at "create\\([ \t\r\n\f]+or[ \t\r\n\f]+replace\\)?")
+ (looking-at "alter"))
+ (not (sqlind-in-comment-or-string (point))))
+ (setq real-start (point))))
+ (goto-char real-start))
+
+ (when (null sqlind-end-stmt-stack)
+ (throw 'finished (list 'defun-start proc-name)))
+
+ (cl-destructuring-bind (pos kind label) (pop sqlind-end-stmt-stack)
+ (unless (and (eq kind nil)
+ (sqlind-labels-match label proc-name))
+ (throw 'finished
+ (list 'syntax-error "bad end label for defun" (point) pos)))))))))
+
+(defun sqlind-maybe-exception-statement ()
+ "If (point) is on an exception keyword, report its syntax.
+See also `sqlind-beginning-of-block'"
+ (when (and (looking-at "exception")
+ (null sqlind-end-stmt-stack))
+ ;; Exception is both a keyword and a type. We need to only stop on the
+ ;; keyword. We detect that if the previous token is either ";" or
+ ;; "BEGIN".
+ (save-excursion
+ (forward-char -1)
+ (sqlind-backward-syntactic-ws)
+ (when (or (looking-at ";")
+ (progn (forward-word -1) (looking-at "\\<_begin\\_>")))
+ (throw 'finished (list 'in-block 'exception))))))
+
+(defun sqlind-maybe-$$-statement ()
+ "If (point) is on a PostgreSQL $$ quote, report its syntax.
+
+PostgreSQL uses $$ to delimit SQL blocks in create statements,
+this function tries to determine if the $$ block is a begin or an
+end block and creates the appropiate syntactic context.
+
+See also `sqlind-beginning-of-block'"
+ (when (looking-at "\\$\\$")
+ (prog1 t
+ (let* ((saved-pos (point)))
+ (ignore-errors (forward-char -1))
+ (sqlind-backward-syntactic-ws)
+ (cond ((looking-at ";")
+ ;; Assume the $$ is ending a statement (previous line is a ';'
+ ;; which ends another statement)
+ (push (list saved-pos '$$ "") sqlind-end-stmt-stack)
+ (goto-char saved-pos))
+ ((null sqlind-end-stmt-stack)
+ (sqlind-beginning-of-statement)
+ (let ((syntax (catch 'finished
+ (sqlind-maybe-create-statement)
+ (sqlind-maybe-defun-statement)
+ 'toplevel)))
+ (if (and (listp syntax) (eq (nth 0 syntax) 'defun-start))
+ (throw 'finished (list 'in-begin-block 'defun (nth 1 syntax)))
+ (throw 'finished (list 'in-begin-block syntax nil)))))
+ (t
+ (sqlind-beginning-of-statement)
+ (cl-destructuring-bind (pos kind _label) (pop sqlind-end-stmt-stack)
+ (unless (eq kind '$$)
+ (throw 'finished
+ (list 'syntax-error "bad closing for $$ begin block" (point) pos))))))))))
+
+(defconst sqlind-start-block-regexp
+ (concat "\\(\\_<"
+ (regexp-opt '("if" "then" "else" "elsif" "elseif" "loop"
+ "begin" "declare" "create" "alter" "exception"
+ "procedure" "function" "end" "case") t)
+ "\\_>\\)\\|)\\|\\$\\$")
+ "Regexp to match the start of a block.")
+
+(defun sqlind-beginning-of-block (&optional end-statement-stack)
+ "Find the start of the current block and return its syntax.
+
+END-STATEMENT-STACK contains a list of \"end\" syntaxes in
+reverse order (a stack) and is used to skip over nested blocks."
+ (interactive)
+ ;; This function works as follows: `sqlind-start-block-regexp` defines the
+ ;; keywords where it stops to inspect the code. Each time it stops at one
+ ;; of these keywords, it checks to see if the keyword is inside a comment or
+ ;; string. If the keyworkd is not inside a comment or string, the
+ ;; `sqlind-maybe-*` functions are called to check if the keyword is valid.
+ ;; Each of these functions will do one of the following:
+ ;;
+ ;; * throw a syntax object with a 'finished tag, if they decide that the
+ ;; keyword is valid
+ ;;
+ ;; * return t to indicate that they decided that the keyword is not valid
+ ;; and `sqlind-beginning-of-block` should search for the next keyword
+ ;;
+ ;; * return nil to indicate that they don't recognize the keyword and
+ ;; another `sqlind-maybe-*` function should be called
+ ;;
+ ;; Some of these `sqlind-maybe-*` functions are specific to the
+ ;; `sql-product` and are only invoked for the speficied SQL dialect.
+
+ (catch 'finished
+ (let ((sqlind-end-stmt-stack end-statement-stack))
+ (while (re-search-backward sqlind-start-block-regexp sqlind-search-limit 'noerror)
+ (or (sqlind-in-comment-or-string (point))
+ (when (looking-at ")") (forward-char 1) (forward-sexp -1) t)
+ (sqlind-maybe-end-statement)
+ (sqlind-maybe-if-statement)
+ (sqlind-maybe-case-statement)
+ (sqlind-maybe-then-statement)
+ (sqlind-maybe-exception-statement)
+ (sqlind-maybe-else-statement)
+ (sqlind-maybe-loop-statement)
+ (sqlind-maybe-begin-statement)
+ (when (eq sql-product 'oracle) ; declare statements only start blocks in PL/SQL
+ (sqlind-maybe-declare-statement))
+ (when (eq sql-product 'postgres)
+ (sqlind-maybe-$$-statement))
+ (sqlind-maybe-create-statement)
+ (sqlind-maybe-defun-statement))))
+ 'toplevel))
+
+;;;;; Determine the syntax inside a case expression
+
+(defconst sqlind-case-clauses-regexp
+ "\\_<\\(when\\|then\\|else\\|end\\)\\_>")
+
+(defun sqlind-syntax-in-case (pos start)
+ "Return the syntax at POS which is part a \"case\" expression at START."
+ (save-excursion
+ (goto-char pos)
+ (cond ((looking-at "when\\|else")
+ ;; A WHEN, or ELSE clause is indented relative to the start of the
+ ;; case expression
+ (cons 'case-clause start))
+ ((looking-at "end")
+ (cons (list 'block-end 'case "") start))
+ ((looking-at "then")
+ ;; THEN and ELSE clauses are indented relative to the start of the
+ ;; when clause, which we must find
+ (while (not (and (re-search-backward "\\_<when\\_>")
+ (sqlind-same-level-statement (point) start)))
+ nil)
+ (cons 'case-clause-item (point)))
+ (t
+ ;; It is a statement continuation from the closest CASE element
+ (while (and (not (= (point) start))
+ (not (and (re-search-backward sqlind-case-clauses-regexp start 'noerror)
+ (sqlind-same-level-statement (point) start))))
+ nil)
+ (cons 'case-clause-item-cont (point))))))
+
+;;;;; Determine the syntax inside a with statement
+
+(defconst sqlind-with-clauses-regexp
+ "\\_<\\(with\\|recursive\\)\\_>")
+
+(defun sqlind-syntax-in-with (pos start)
+ "Return the syntax at POS which is part of a \"with\" statement at START."
+ (save-excursion
+ (catch 'finished
+ (goto-char pos)
+ (cond
+ ((looking-at sqlind-with-clauses-regexp)
+ (throw 'finished (cons 'with-clause start)))
+ ((and (looking-at "\\_<select\\_>")
+ (sqlind-same-level-statement (point) start))
+ (throw 'finished (cons 'with-clause start))))
+ (while (re-search-backward "\\_<select\\_>" start 'noerror)
+ (when (sqlind-same-level-statement (point) start)
+ (throw 'finished (sqlind-syntax-in-select pos (point)))))
+ (goto-char pos)
+ (when (looking-at "\\_<as\\_>")
+ (throw 'finished (cons 'with-clause-cte-cont start)))
+ (sqlind-backward-syntactic-ws)
+ (when (looking-at ",")
+ (throw 'finished (cons 'with-clause-cte start)))
+ (forward-word -1)
+ (when (looking-at sqlind-with-clauses-regexp)
+ ;; We're right after the with (recursive keyword)
+ (throw 'finished (cons 'with-clause-cte start)))
+ (throw 'finished (cons 'with-clause-cte-cont start)))))
+
+;;;;; Determine the syntax inside a select statement
+
+(defconst sqlind-select-clauses-regexp
+ (concat
+ "\\_<\\("
+ "\\(\\(union\\(\\s-+all\\)?\\)\\|intersect\\|minus\\|except\\)?[ \t\r\n\f]*select\\|"
+ "\\(bulk[ \t\r\n\f]+collect[ \t\r\n\f]+\\)?into\\|"
+ "from\\|"
+ "where\\|"
+ "order[ \t\r\n\f]+by\\|"
+ "having\\|"
+ "group[ \t\r\n\f]+by\\|"
+ "connect[ \t\r\n\f]+by\\|"
+ "start[ \t\r\n\f]+with\\|"
+ "limit"
+ "\\)\\_>"))
+
+(defconst sqlind-select-join-regexp
+ (regexp-opt '("inner" "left" "right" "natural" "cross" "full") 'symbols))
+
+(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
+ (catch 'finished
+ (goto-char pos)
+
+ ;; all select query components are indented relative to the
+ ;; start of the select statement)
+ (when (looking-at sqlind-select-clauses-regexp)
+ (throw 'finished (cons 'select-clause start)))
+
+ ;; when we are not looking at a select component, find the
+ ;; nearest one from us.
+
+ (while (sqlind-search-backward (point) sqlind-select-clauses-regexp start)
+ (let* ((match-pos (match-beginning 0))
+ (clause (sqlind-match-string 0)))
+ (setq clause (replace-regexp-in-string "[ \t\r\n\f]" " " clause))
+ (when (sqlind-same-level-statement (point) start)
+ (cond
+ ((looking-at "select\\(\\s *\\_<\\(top\\s +[0-9]+\\|distinct\\|unique\\)\\_>\\)?")
+ ;; we are in the column selection section.
+ (goto-char pos)
+ (if (looking-at ",")
+ (throw 'finished (cons 'select-column match-pos))
+ (progn
+ (sqlind-backward-syntactic-ws)
+ (throw 'finished
+ (cons (if (or (eq (match-end 0) (1+ (point)))
+ (looking-at ","))
+ 'select-column
+ 'select-column-continuation)
+ match-pos)))))
+
+ ((looking-at "from")
+ ;; FROM is only keyword if the previous char is NOT a
+ ;; comma ','
+ (forward-char -1)
+ (sqlind-backward-syntactic-ws)
+ (unless (looking-at ",")
+ ;; We are in the from section. If this line starts with 'on'
+ ;; or the previous line ends with 'on' we have a join
+ ;; condition
+ (goto-char pos)
+ (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
+ (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
+ (goto-char pos)
+ (when (or (looking-at ",")
+ (progn
+ (sqlind-backward-syntactic-ws)
+ (looking-at ",")))
+ (throw 'finished (cons 'select-table match-pos)))
+
+ ;; otherwise, we continue the table definition from the
+ ;; previous line.
+ (throw 'finished (cons 'select-table-continuation match-pos)))
+
+ (t
+ (throw 'finished
+ (cons (list 'in-select-clause clause) match-pos))))))))))
+
+
+;;;;; Determine the syntax inside an insert statement
+
+(defconst sqlind-insert-clauses-regexp
+ "\\_<\\(insert\\([ \t\r\n\f]+into\\)?\\|values\\|select\\)\\_>")
+
+(defun sqlind-syntax-in-insert (pos start)
+ "Return the syntax at POS which is inside an \"insert\" statement at START."
+
+ ;; The insert clause is really easy since it has the form insert into TABLE
+ ;; (COLUMN_LIST) values (VALUE_LIST) or insert into TABLE select
+ ;; SELECT_CLAUSE
+ ;;
+ ;; note that we will never be called when point is in COLUMN_LIST or
+ ;; VALUE_LIST, as that is a nested-statement-continuation which starts with
+ ;; the open paranthesis.
+ ;;
+ ;; if we are inside the SELECT_CLAUSE, we delegate the syntax to
+ ;; `sqlind-syntax-in-select'
+
+ (save-excursion
+ (catch 'finished
+ (goto-char pos)
+
+ ;; all select query components are indented relative to the
+ ;; start of the select statement)
+ (when (looking-at sqlind-insert-clauses-regexp)
+ (throw 'finished (cons 'insert-clause start)))
+
+ (while (re-search-backward sqlind-insert-clauses-regexp start t)
+ (let* ((match-pos (match-beginning 0))
+ (clause (sqlind-match-string 0)))
+ (setq clause (replace-regexp-in-string "[ \t\r\n\f]" " " clause))
+ (when (sqlind-same-level-statement (point) start)
+ (throw 'finished
+ (if (looking-at "select")
+ (sqlind-syntax-in-select pos match-pos)
+ (cons (list 'in-insert-clause clause) match-pos)))))))))
+
+
+;;;;; Determine the syntax inside a delete statement
+
+(defconst sqlind-delete-clauses-regexp
+ "\\_<\\(delete\\([ \t\r\n\f]+from\\)?\\|where\\|returning\\|\\(bulk[ \t\r\n\f]collect[ \t\r\n\f]\\)?into\\)\\_>")
+
+(defun sqlind-syntax-in-delete (pos start)
+ "Return the syntax at POS which is inside a \"delete\" statement at START."
+ (save-excursion
+ (catch 'finished
+ (goto-char pos)
+
+ ;; all select query components are indented relative to the
+ ;; start of the select statement)
+ (when (looking-at sqlind-delete-clauses-regexp)
+ (throw 'finished (cons 'delete-clause start)))
+
+ (while (re-search-backward sqlind-delete-clauses-regexp start t)
+ (let* ((match-pos (match-beginning 0))
+ (clause (sqlind-match-string 0)))
+ (setq clause (replace-regexp-in-string "[ \t\r\n\f]" " " clause))
+ (when (sqlind-same-level-statement (point) start)
+ (throw 'finished
+ (cons (list 'in-delete-clause clause) match-pos))))))))
+
+
+;;;;; Determine the syntax inside an update statement
+
+(defconst sqlind-update-clauses-regexp
+ "\\_<\\(update\\|set\\|where\\)\\_>")
+
+(defun sqlind-syntax-in-update (pos start)
+ "Return the syntax at POS which is inside an \"update\" statement at START."
+ (save-excursion
+ (catch 'finished
+ (goto-char pos)
+
+ ;; all select query components are indented relative to the start of the
+ ;; select statement)
+ (when (looking-at sqlind-update-clauses-regexp)
+ (throw 'finished (cons 'update-clause start)))
+
+ (while (re-search-backward sqlind-update-clauses-regexp start t)
+ (let* ((match-pos (match-beginning 0))
+ (clause (sqlind-match-string 0)))
+ (setq clause (replace-regexp-in-string "[ \t\r\n\f]" " " clause))
+ (when (sqlind-same-level-statement (point) start)
+ (throw 'finished
+ (cons (list 'in-update-clause clause) match-pos))))))))
+
+
+;;;;; Refine the syntax of an end statement.
+
+(defun sqlind-refine-end-syntax (end-kind end-label end-pos context)
+ "Return a 'block-end syntax for a line containing an \"end\" keyword.
+
+END-KIND contains the symbol after the end statement ('if if the
+statement is an \"end if\", 'loop if the statement is an \"end
+loop\", etc). This can be nil if there is a plain \"end\"
+keyword.
+
+END-LABEL contains the label of the end statement, if there is
+one.
+
+END-POS is the position where the \"end\" keyword is.
+
+CONTEXT represents the syntactic context up to the current line.
+
+The function will perform some validations, for example, an \"end
+if\" must close an if statement, an \"end loop\" must close a
+loop, etc. It will return a 'syntax-error syntax if the
+validation fails.
+
+If all checks pass, it will return a (block-end KIND LABEL) where
+KIND is the symbol determining the type of the block ('if, 'loop,
+'procedure, etc) and LABEL is the block label, if there is any."
+ (catch 'done
+
+ (when (null context) ; can happen
+ (throw 'done
+ (cons (list 'syntax-error "end statement closes nothing"
+ end-pos end-pos)
+ end-pos)))
+
+ (let ((syntax (sqlind-syntax context))
+ (anchor (sqlind-anchor-point context))
+ (syntax-symbol (sqlind-syntax-symbol context)))
+ (cond
+ ((memq syntax-symbol '(package package-body))
+ ;; we are closing a package declaration or body, `end-kind' must be
+ ;; empty, `end-label' can be empty or it must match the package name
+ (throw 'done
+ (cons
+ (cond (end-kind ; no end-kind is allowed for a package
+ (list 'syntax-error
+ "bad closing for package" anchor end-pos))
+ ((sqlind-labels-match end-label (nth 1 syntax))
+ (list 'block-end syntax-symbol (nth 1 syntax)))
+ (t
+ (list 'syntax-error "mismatched end label for package"
+ anchor end-pos)))
+ anchor)))
+
+ ((eq syntax-symbol 'in-begin-block)
+ ;; we are closing a begin block (either toplevel, procedure/function
+ ;; or a simple begin block. `end-kind' must be empty, `end-label'
+ ;; can be empty or it must match the pakage-name
+ (let ((block-label (nth 2 syntax)))
+ (throw 'done
+ (cons
+ (cond (end-kind ; no end-kind is allowed for a begin block
+ (list 'syntax-error
+ "bad closing for begin block" anchor end-pos))
+ ((sqlind-labels-match end-label block-label)
+ (list 'block-end (nth 1 syntax) block-label))
+ (t
+ (list 'syntax-error "mismatched end label for block"
+ anchor end-pos)))
+ anchor))))
+
+ ((eq syntax-symbol 'in-block)
+ (let ((block-kind (nth 1 syntax))
+ (block-label (nth 2 syntax)))
+ (cond
+ ((eq block-kind 'exception)
+ (goto-char anchor)
+ (throw 'done
+ (sqlind-refine-end-syntax
+ end-kind end-label end-pos (sqlind-syntax-of-line))))
+
+ ((eq block-kind 'loop)
+ (throw 'done
+ (cons
+ (cond ((not (eq end-kind 'loop))
+ (list 'syntax-error "bad closing for loop block"
+ anchor end-pos))
+ ((not (sqlind-labels-match end-label block-label))
+ (list 'syntax-error "mismatched end label for loop"
+ anchor end-pos))
+ (t
+ (list 'block-end block-kind block-label)))
+ anchor)))
+
+ ((eq block-kind 'then)
+ (goto-char anchor)
+
+ (catch 'found
+ (while t
+ (let ((then-context (sqlind-syntax-of-line)))
+ (goto-char (cdar then-context))
+ (cond
+ ((looking-at "when\\_>\\|then\\_>") t)
+ ((looking-at "\\(?:<<\\([a-z0-9_]+\\)>>[ \t\r\n\f]*\\)?\\_<\\(if\\|case\\)\\_>")
+ (throw 'found t))
+ (t
+ (throw 'done
+ (cons
+ (list 'syntax-error "bad syntax start for then keyword"
+ (point) (point))
+ anchor)))))))
+
+ (let ((start-label (or (sqlind-match-string 1) ""))
+ (start-kind (intern (sqlind-match-string 2))))
+ (throw 'done
+ (cons
+ (cond ((not (or (null end-kind) (eq end-kind start-kind)))
+ (list 'syntax-error "bad closing for if/case block"
+ (point) end-pos))
+ ((not (sqlind-labels-match end-label start-label))
+ (list 'syntax-error "mismatched labels for if/case block"
+ (point) end-pos))
+ (t
+ (list 'block-end start-kind start-label)))
+ anchor))))
+
+ ((memq block-kind '(else elsif elseif))
+ ;; search the enclosing then context and refine form there. The
+ ;; `cdr' in sqlind-syntax-of-line is used to remove the
+ ;; block-start context for the else clause
+ (goto-char anchor)
+ (throw 'done
+ (sqlind-refine-end-syntax
+ end-kind end-label end-pos (cdr (sqlind-syntax-of-line)))))
+
+ ((memq block-kind '(if case))
+ (throw 'done
+ (cons
+ (cond ((not (eq end-kind block-kind))
+ (list 'syntax-error "bad closing for if/case block"
+ anchor end-pos))
+ ((not (sqlind-labels-match end-label block-label))
+ (list 'syntax-error
+ "bad closing for if/case block (label mismatch)"
+ anchor end-pos))
+ (t (list 'block-end block-kind block-label)))
+ anchor)))
+ )))
+
+ ((memq syntax-symbol '(block-start comment-start))
+ ;; there is a more generic context following one of these
+ (throw 'done
+ (sqlind-refine-end-syntax
+ end-kind end-label end-pos (cdr context))))
+
+ ((eq syntax-symbol 'defun-start)
+ (throw 'done
+ (cons
+ (if (and (null end-kind)
+ (sqlind-labels-match end-label (nth 1 syntax)))
+ (list 'block-end 'defun end-label)
+ (list 'syntax-error "mismatched end label for defun"
+ anchor end-pos))
+ anchor)))
+
+ ((eq syntax-symbol 'block-end)
+ (goto-char anchor)
+ (forward-line -1)
+ (throw 'done
+ (sqlind-refine-end-syntax
+ end-kind end-label end-pos (sqlind-syntax-of-line)))))
+
+ ;; if the above cond fell through, we have a syntax error
+ (cons (list 'syntax-error "end statement closes nothing"
+ end-pos end-pos)
+ anchor))))
+
+
+;;;;; sqlind-syntax-of-line
+
+(defun sqlind-refine-syntax (context pos have-block-context)
+ "Refine a basic syntax CONTEXT at POS.
+CONTEXT is a syntactic context obtained by looking at the
+statement start and block start, see `sqlind-syntax-of-line'. We
+refine it by looking at the contents of the current line and the
+contents of the anchor.
+
+HAVE-BLOCK-CONTEXT indicates that we are indenting a statement,
+not a statement-continuation POS is the same as the
+`sqlind-beginning-of-statement'."
+ (let ((syntax (sqlind-syntax context))
+ (anchor (sqlind-anchor-point context))
+ (syntax-symbol (sqlind-syntax-symbol context)))
+
+ (goto-char pos)
+
+ (cond
+ ;; do we start a comment?
+ ((and (not (eq syntax-symbol 'comment-continuation))
+ (looking-at sqlind-comment-start-skip))
+ (push (cons 'comment-start anchor) context))
+
+ ;; Refine a statement continuation
+ ((memq syntax-symbol '(statement-continuation nested-statement-continuation))
+
+ ;; a (nested) statement continuation which starts with loop
+ ;; or then is a block start
+ (if (and have-block-context (looking-at "\\(loop\\|then\\|when\\)\\_>"))
+ (push (cons (list 'block-start (intern (sqlind-match-string 0))) anchor)
+ context)
+ ;; else
+ (goto-char anchor)
+ (when (eq syntax 'nested-statement-continuation)
+ (forward-char 1)
+ (sqlind-forward-syntactic-ws)
+ (setq anchor (point)))
+
+ ;; when all we have before `pos' is a label, we have a
+ ;; labeled-statement-start
+ (when (looking-at "<<\\([a-z0-9_]+\\)>>")
+ (goto-char (match-end 0))
+ (forward-char 1)
+ (sqlind-forward-syntactic-ws)
+ (when (eq (point) pos)
+ (push (cons 'labeled-statement-start anchor) context)))
+
+ (when (looking-at "when\\_>")
+ (let* ((acontext (sqlind-syntax-of-line))
+ (asyntax (sqlind-syntax acontext)))
+ (cond ((equal asyntax '(in-block exception ""))
+ (push (cons '(in-block exception-handler "") (point)) context))
+ ((equal asyntax '(block-start when))
+ ;; Refine again in the context of the when line
+ (setq context (sqlind-refine-syntax (cdr acontext) pos have-block-context))))))
+
+ ;; maybe we have a DML statement (select, insert, update and
+ ;; delete)
+
+ ;; skip a cursor definition if it is before our point, in the
+ ;; following format:
+ ;;
+ ;; CURSOR name IS
+ ;; CURSOR name type IS
+ (when (looking-at "cursor\\b")
+ (let ((origin (point)))
+ (forward-sexp 3)
+ (sqlind-forward-syntactic-ws)
+ (when (looking-at "is\\b")
+ (goto-char (match-end 0))
+ (sqlind-forward-syntactic-ws))
+ (unless (<= (point) pos)
+ (goto-char origin))))
+
+ ;; skip a forall statement if it is before our point
+ (when (looking-at "forall\\b")
+ (when (re-search-forward "\\b\\(select\\|update\\|delete\\|insert\\)\\b" pos 'noerror)
+ (goto-char (match-beginning 0))))
+
+ ;; only check for syntax inside DML clauses if we are not
+ ;; at the start of one.
+ (when (< (point) pos)
+ (cond
+ ;; NOTE: We only catch here "CASE" expressions, not CASE
+ ;; statements. We also catch assignments with case (var
+ ;; := CASE ...)
+ ((looking-at "\\(\\w+[ \t\r\n\f]+:=[ \t\r\n\f]+\\)?\\(case\\)")
+ (when (< (match-beginning 2) pos)
+ (push (sqlind-syntax-in-case pos (match-beginning 2)) context)))
+ ((looking-at "with")
+ (push (sqlind-syntax-in-with pos (point)) context))
+ ((looking-at "select")
+ (push (sqlind-syntax-in-select pos (point)) context))
+ ((looking-at "insert")
+ (push (sqlind-syntax-in-insert pos (point)) context))
+ ((looking-at "delete")
+ (push (sqlind-syntax-in-delete pos (point)) context))
+ ((looking-at "update")
+ (push (sqlind-syntax-in-update pos (point)) context))))
+
+ (when (eq (sqlind-syntax-symbol context) 'select-column-continuation)
+ (let ((cdef (sqlind-column-definition-start pos (sqlind-anchor-point context))))
+ (when cdef
+ (save-excursion
+ (goto-char cdef)
+ (when (looking-at "case")
+ (push (sqlind-syntax-in-case pos (point)) context))))))
+
+ (when (eq (sqlind-syntax-symbol context) 'nested-statement-continuation)
+ (save-excursion
+ ;; Look for a join expression inside a nested statement, see #70
+ (goto-char pos)
+ (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
+ (let ((jstart (sqlind-find-join-start (point) anchor)))
+ (when jstart
+ (push (cons 'select-join-condition jstart) context))))))
+
+ ))
+
+ ;; create block start syntax if needed
+
+ ((and (eq syntax-symbol 'in-block)
+ (memq (nth 1 syntax) '(if elsif elseif then case))
+ (looking-at "\\(then\\|\\(els\\(e\\|e?if\\)\\)\\)\\_>"))
+ (let ((what (intern (sqlind-match-string 0))))
+ ;; the only invalid combination is a then statement in
+ ;; an (in-block "then") context
+ (unless (and (eq what 'then) (equal (nth 1 syntax) 'then))
+ (push (cons (list 'block-start what) anchor) context))))
+
+ ((and (eq syntax-symbol 'in-block)
+ (eq (nth 1 syntax) 'exception)
+ (not (looking-at "\\(when\\|end\\)\\_>")))
+ (save-excursion
+ (when (sqlind-search-backward pos "when\\_>" anchor)
+ (push (cons (list 'in-block 'exception-handler) (point)) context))))
+
+ ;; note that begin is not a block-start in a 'in-begin-block
+ ;; context
+ ((and (memq syntax-symbol '(defun-start declare-statement toplevel
+ package package-body))
+ (or (looking-at "begin\\_>")
+ (and (eq sql-product 'postgres) (looking-at "\\$\\$"))))
+ (push (cons (list 'block-start 'begin) anchor) context))
+
+ ((and (memq syntax-symbol '(defun-start package package-body))
+ (looking-at "\\(is\\|as\\)\\_>"))
+ (push (cons (list 'block-start 'is-or-as) anchor) context))
+
+ ((and (memq syntax-symbol '(in-begin-block in-block))
+ (looking-at "exception\\_>"))
+ (push (cons (list 'block-start 'exception) anchor) context))
+
+ ((and (eq syntax-symbol 'in-block)
+ (memq (nth 1 syntax) '(then case)))
+ (if (looking-at "when\\_>")
+ (push (cons (list 'block-start 'when) anchor) context)
+ ;; NOTE: the "when" case is handed above
+ (when (sqlind-search-backward pos "when\\_>" anchor)
+ (push (cons '(in-block when) (point)) context))))
+
+ ;; indenting the select clause inside a view or a "create table as"
+ ;; statement.
+ ((and (eq syntax-symbol 'create-statement)
+ (memq (nth 1 syntax) '(view table)))
+ (goto-char anchor)
+ (catch 'done
+ (while (re-search-forward "\\bselect\\b" pos 'noerror)
+ (goto-char (match-beginning 0))
+ (when (sqlind-same-level-statement (point) anchor)
+ (push (sqlind-syntax-in-select pos (point)) context)
+ (throw 'done nil))
+ (goto-char (match-end 0)))))
+
+ ;; create a block-end syntax if needed
+
+ ((and (not (eq syntax-symbol 'comment-continuation))
+ (looking-at "end[ \t\r\n\f]*\\(\\_<\\(?:if\\|loop\\|case\\)\\_>\\)?[ \t\r\n\f]*\\(\\_<\\(?:[a-z0-9_]+\\)\\_>\\)?"))
+ ;; so we see the syntax which closes the current block. We still
+ ;; need to check if the current end is a valid closing block
+ (let ((kind (or (sqlind-match-string 1) ""))
+ (label (or (sqlind-match-string 2) "")))
+ (push (sqlind-refine-end-syntax
+ (if (equal kind "") nil (intern kind))
+ label (point) context)
+ context)))
+
+ ((and (eq sql-product 'postgres)
+ (not (eq syntax-symbol 'comment-continuation))
+ (looking-at "\\$\\$"))
+ (push (sqlind-refine-end-syntax
+ nil "" (point) context)
+ context))
+ )
+ context))
+
+(defun sqlind-syntax-of-line ()
+ "Return the syntax at the start of the current line.
+The function returns a list of (SYNTAX . ANCHOR) cons cells.
+SYNTAX is either a symbol or a list starting with a symbol,
+ANCHOR is a buffer position which is the reference for the
+SYNTAX. `sqlind-indentation-syntax-symbols' lists the syntax
+symbols and their meaning.
+
+The first element in the list is the most specific syntax for the
+line, the remaining elemens are more generic ones. For example,
+a line can be inside an SELECT clause which itself is inside a
+procedure block."
+ (save-excursion
+ (with-syntax-table sqlind-syntax-table
+ (let* ((pos (progn (back-to-indentation) (point)))
+ (sqlind-search-limit (sqlind-beginning-of-directive))
+ (context-start (progn (sqlind-beginning-of-statement) (point)))
+ (context (list (cons 'statement-continuation context-start)))
+ (have-block-context nil))
+
+ (goto-char context-start)
+ (when (or (>= context-start pos)
+ (and (looking-at sqlind-start-block-regexp)
+ (not (sqlind-looking-at-begin-transaction))))
+ (goto-char pos)
+ ;; if we are at the start of a statement, or the nearest statement
+ ;; starts after us, make the enclosing block the starting context
+ (setq have-block-context t)
+ (let ((block-info (sqlind-beginning-of-block)))
+
+ ;; certain kind of blocks end within a statement
+ ;; (e.g. create view). If we found one of those blocks and
+ ;; it is not within our statement, we ignore the block info.
+
+ (if (and (listp block-info)
+ (eq (nth 0 block-info) 'create-statement)
+ (not (memq (nth 1 block-info) '(function procedure)))
+ (not (eql context-start (point))))
+ (progn
+ (setq context-start (point-min))
+ (setq context (list (cons 'toplevel context-start))))
+ ;; else
+ (setq context-start (point))
+ (setq context (list (cons block-info context-start))))))
+
+ (let ((parse-info (syntax-ppss pos)))
+ (cond ((nth 4 parse-info) ; inside a comment
+ (push (cons 'comment-continuation (nth 8 parse-info)) context))
+ ((nth 3 parse-info) ; inside a string
+ (push (cons 'string-continuation (nth 8 parse-info)) context))
+ ((> (nth 0 parse-info) 0) ; nesting
+ (let ((start (nth 1 parse-info)))
+ (goto-char (1+ start))
+ (skip-chars-forward " \t\r\n\f\v" pos)
+ (if (eq (point) pos)
+ (push (cons 'nested-statement-open start) context)
+ (goto-char pos)
+ (if (looking-at ")")
+ (push (cons 'nested-statement-close start) context)
+ (push (cons 'nested-statement-continuation start) context)))))))
+
+ ;; now let's refine the syntax by adding info about the current line
+ ;; into the mix.
+ (sqlind-refine-syntax context pos have-block-context)))))
+
+
+(defun sqlind-show-syntax-of-line ()
+ "Print the syntax of the current line."
+ (interactive)
+ (prin1 (sqlind-syntax-of-line)))
+
+\f
+;;;; Indentation of SQL code
+;;;;; Indentation calculation routines
+(defvar sqlind-basic-offset 2
+ "The basic indentaion amount for SQL code.
+Indentation is usually done in multiples of this amount, but
+special indentation functions can do other types of indentation
+such as aligning. See also `sqlind-indentation-offsets-alist'.")
+
+(make-variable-buffer-local 'sqlind-basic-offset)
+
+(defvar sqlind-indentation-syntax-symbols '()
+ "This variable exists just for its documentation.
+
+The the SQL parsing code returns a syntax definition (either a
+symbol or a list) and an anchor point, which is a buffer
+position. The syntax symbols can be used to define how to indent
+each line, see `sqlind-indentation-offsets-alist'
+
+The following syntax symbols are defined for SQL code:
+
+- (syntax-error MESSAGE START END) -- this is returned when the
+ parse failed. MESSAGE is an informative message, START and END
+ are buffer locations denoting the problematic region. ANCHOR
+ is undefined for this syntax info
+
+- `in-comment' -- line is inside a multi line comment, ANCHOR is
+ the start of the comment.
+
+- `comment-start' -- line starts with a comment. ANCHOR is the
+ start of the enclosing block.
+
+- `in-string' -- line is inside a string, ANCHOR denotes the start
+ of the string.
+
+- `toplevel' -- line is at toplevel (not inside any programming
+ construct). ANCHOR is usually (point-min).
+
+- (in-block BLOCK-KIND LABEL) -- line is inside a block
+ construct. BLOCK-KIND (a symbol) is the actual block type and
+ can be one of \"if\", \"case\", \"exception\", \"loop\" etc.
+ If the block is labeled, LABEL contains the label. ANCHOR is
+ the start of the block.
+
+- (in-begin-block KIND LABEL) -- line is inside a block started
+ by a begin statement. KIND (a symbol) is \"toplevel-block\"
+ for a begin at toplevel, \"defun\" for a begin that starts the
+ body of a procedure or function, \"package\" for a begin that
+ starts the body of a package, or nil for a begin that is none
+ of the previous. For a \"defun\" or \"package\", LABEL is the
+ name of the procedure, function or package, for the other block
+ types LABEL contains the block label, or the empty string if
+ the block has no label. ANCHOR is the start of the block.
+
+- (block-start KIND) -- line begins with a statement that starts
+ a block. KIND (a symbol) can be one of \"then\", \"else\" or
+ \"loop\". ANCHOR is the reference point for the block
+ start (the coresponding if, case, etc).
+
+- (block-end KIND LABEL) -- the line contains an end statement.
+ KIND (a symbol) is the type of block we are closing, LABEL (a
+ string) is the block label (or procedure name for an end
+ defun).
+
+- declare-statement -- line is after a declare keyword, but
+ before the begin. ANCHOR is the start of the declare
+ statement.
+
+- (package NAME) -- line is inside a package definition. NAME is
+ the name of the package, ANCHOR is the start of the package.
+
+- (package-body NAME) -- line is inside a package body. NAME is
+ the name of the package, ANCHOR is the start of the package
+ body.
+
+- (create-statement WHAT NAME) -- line is inside a CREATE
+ statement (other than create procedure or function). WHAT is
+ the thing being created, NAME is its name. ANCHOR is the start
+ of the create statement.
+
+- (defun-start NAME) -- line is inside a procedure of function
+ definition but before the begin block that starts the body.
+ NAME is the name of the procedure/function, ANCHOR is the start
+ of the procedure/function definition.
+
+The following SYNTAX-es are for SQL statements. For all of
+them ANCHOR points to the start of a statement itself.
+
+- labeled-statement-start -- line is just after a label.
+
+- statement-continuation -- line is inside a statement which
+ starts on a previous line.
+
+- nested-statement-open -- line is just inside an opening
+ bracket, but the actual bracket is on a previous line.
+
+- nested-statement-continuation -- line is inside an opening
+ bracket, but not the first element after the bracket.
+
+- nested-statement-close -- line is inside an opening bracket and
+ the line contains the closing bracket as the first character.
+
+The following SYNTAX-es are for statements which are SQL
+code (DML statements). They are pecialisations on the previous
+statement syntaxes and for all of them a previous generic
+statement syntax is present earlier in the SYNTAX list. Unless
+otherwise specified, ANCHOR points to the start of the
+clause (select, from, where, etc) in which the current point is.
+
+- with-clause -- line is inside a WITH clause, but before the
+ main SELECT clause.
+
+- with-clause-cte -- line is inside a with clause before a
+ CTE (common table expression) declaration
+
+- with-clause-cte-cont -- line is inside a with clause before a
+ CTE definition
+
+- case-clause -- line is on a CASE expression (WHEN or END
+ clauses). ANCHOR is the start of the CASE expression.
+
+- case-clause-item -- line is on a CASE expression (THEN and ELSE
+ clauses). ANCHOR is the position of the case clause.
+
+- case-clause-item-cont -- line is on a CASE expression but not
+ on one of the CASE sub-keywords. ANCHOR points to the case
+ keyword that this line is a continuation of.
+
+- select-clause -- line is inside a select statement, right
+ before one of its clauses (from, where, order by, etc).
+
+- select-column -- line is inside the select column section,
+ after a full column was defined (and a new column definition is
+ about to start).
+
+- select-column-continuation -- line is inside the select column
+ section, but in the middle of a column definition. The defined
+ column starts on a previous like. Note that ANCHOR still
+ points to the start of the select statement itself.
+
+- select-join-condition -- line is right before or just after the ON clause
+ for an INNER, LEFT or RIGHT join. ANCHOR points to the join statement
+ for which the ON is defined.
+
+- select-table -- line is inside the from clause, just after a
+ table was defined and a new one is about to start.
+
+- select-table-continuation -- line is inside the from clause,
+ inside a table definition which starts on a previous line. Note
+ that ANCHOR still points to the start of the select statement
+ itself.
+
+- (in-select-clause CLAUSE) -- line is inside the select CLAUSE,
+ which can be \"where\", \"order by\", \"group by\" or
+ \"having\". Note that CLAUSE can never be \"select\" and
+ \"from\", because we have special syntaxes inside those
+ clauses.
+
+- insert-clause -- line is inside an insert statement, right
+ before one of its clauses (values, select).
+
+- (in-insert-clause CLAUSE) -- line is inside the insert CLAUSE,
+ which can be \"insert into\" or \"values\".
+
+- delete-clause -- line is inside a delete statement right before
+ one of its clauses.
+
+- (in-delete-clause CLAUSE) -- line is inside a delete CLAUSE,
+ which can be \"delete from\" or \"where\".
+
+- update-clause -- line is inside an update statement right
+ before one of its clauses.
+
+- (in-update-clause CLAUSE) -- line is inside an update CLAUSE,
+ which can be \"update\", \"set\" or \"where\".")
+
+(defconst sqlind-default-indentation-offsets-alist
+ '((syntax-error sqlind-report-sytax-error)
+ (in-string sqlind-report-runaway-string)
+
+ (comment-continuation sqlind-indent-comment-continuation)
+ (comment-start sqlind-indent-comment-start)
+
+ (toplevel 0)
+ (in-block +)
+ (in-begin-block +)
+ (block-start 0)
+ (block-end 0)
+ (declare-statement +)
+ (package ++)
+ (package-body 0)
+ (create-statement +)
+ (defun-start +)
+ (labeled-statement-start 0)
+ (statement-continuation +)
+ (nested-statement-open sqlind-use-anchor-indentation +)
+ (nested-statement-continuation sqlind-use-previous-line-indentation)
+ (nested-statement-close sqlind-use-anchor-indentation)
+
+ (with-clause sqlind-use-anchor-indentation)
+ (with-clause-cte +)
+ (with-clause-cte-cont ++)
+ (case-clause 0)
+ (case-clause-item sqlind-use-anchor-indentation +)
+ (case-clause-item-cont sqlind-right-justify-clause)
+ (select-clause sqlind-right-justify-clause)
+ (select-column sqlind-indent-select-column)
+ (select-column-continuation sqlind-indent-select-column +)
+ (select-join-condition ++)
+ (select-table sqlind-indent-select-table)
+ (select-table-continuation sqlind-indent-select-table +)
+ (in-select-clause sqlind-lineup-to-clause-end
+ sqlind-right-justify-logical-operator)
+ (insert-clause sqlind-right-justify-clause)
+ (in-insert-clause sqlind-lineup-to-clause-end)
+ (delete-clause sqlind-right-justify-clause)
+ (in-delete-clause sqlind-lineup-to-clause-end)
+ (update-clause sqlind-right-justify-clause)
+ (in-update-clause sqlind-lineup-to-clause-end))
+ "Define the indentation amount for each syntactic symbol.
+
+The value of this variable is an ALIST with the format:
+
+ ((SYNTACTIC-SYMBOL . INDENTATION-OFFSETS) ... )
+
+`sqlind-indentation-syntax-symbols' documents the list of possible
+SYNTACTIC-SYMBOL values.
+
+INDENTATION-OFFSETS defines the adjustments made to the
+indentation for each syntactic-symbol. It is a list of:
+
+ a NUMBER -- the NUMBER will be added to the indentation offset.
+
+ '+ -- the current indentation offset is incremented by
+ `sqlind-basic-offset'
+
+ '++ -- the current indentation offset is indentation by 2 *
+ `sqlind-basic-offset'
+
+ '- -- the current indentation offset is decremented by
+ `sqlind-basic-offset'
+
+ '-- -- the current indentation offset is decremented by 2 *
+ `sqlind-basic-offset'
+
+ a FUNCTION -- the syntax and current indentation offset is
+ passed to the function and its result is used as the new
+ indentation offset.
+
+See `sqlind-calculate-indentation' for how the indentation offset
+is calculated.")
+
+(defvar sqlind-indentation-offsets-alist
+ sqlind-default-indentation-offsets-alist
+ "Define the indentation amount for each syntactic symbol.
+See also `sqlind-default-indentation-offsets-alist',
+`sqlind-indentation-syntax-symbols' and
+`sqlind-calculate-indentation'")
+
+(make-variable-buffer-local 'sqlind-indentation-offsets-alist)
+
+(defun sqlind-calculate-indentation (syntax &optional base-indentation)
+ "Return the indentation that should be applied to the current line.
+SYNTAX is the syntaxtic information as returned by
+`sqlind-syntax-of-line', BASE-INDENTATION is an indentation offset
+to start with. When BASE-INDENTATION is nil, it is initialised
+to the column of the anchor.
+
+The indentation is done as follows: first, the indentation
+offsets for the current syntactic symbol is looked up in
+`sqlind-indentation-offsets-alist'. Than, for each indentation
+offset, BASE-INDENTATION is adjusted according to that
+indentation offset. The final value of BASE-INDENTATION is than
+returned."
+ (if (null syntax)
+ base-indentation
+ ;; else
+
+ ;; when the user did not specify a base-indentation, we use the
+ ;; column of the anchor as a starting point
+ (when (null base-indentation)
+ (setq base-indentation (save-excursion
+ (goto-char (cdar syntax))
+ (current-column))))
+
+ (let* ((syntax-symbol (sqlind-syntax-symbol syntax))
+ (indent-info (cdr (assoc syntax-symbol
+ sqlind-indentation-offsets-alist)))
+ (new-indentation base-indentation))
+
+ ;; the funcall below can create a nil indentation symbol to
+ ;; abort the indentation process
+ (while (and new-indentation indent-info)
+ (let ((i (car indent-info)))
+ (setq new-indentation
+ (cond
+ ((eq i '+) (+ new-indentation sqlind-basic-offset))
+ ((eq i '++) (+ new-indentation (* 2 sqlind-basic-offset)))
+ ((eq i '-) (- new-indentation sqlind-basic-offset))
+ ((eq i '--) (- new-indentation (* 2 sqlind-basic-offset)))
+ ((integerp i) (+ new-indentation i))
+ ((functionp i) (funcall i syntax new-indentation))
+ ;; ignore unknown symbols by default
+ (t new-indentation))))
+ (setq indent-info (cdr indent-info)))
+ new-indentation)))
+
+;;;;; Indentation helper functions
+
+(defun sqlind-report-sytax-error (syntax _base-indentation)
+ "Report a syntax error for a 'syntax-error SYNTAX."
+ (cl-destructuring-bind (_sym msg start end) (sqlind-syntax syntax)
+ (message "%s (%d %d)" msg start end))
+ nil)
+
+(defun sqlind-report-runaway-string (_syntax _base-indentation)
+ "Report an error for a runaway string.
+This is a string that extends over multiple lines. This is not
+supported in SQL."
+ (message "runaway string constant")
+ nil)
+
+(defun sqlind-use-anchor-indentation (syntax _base-indentation)
+ "Return the indentation of the line containing anchor of SYNTAX.
+By default, the column of the anchor position is uses as a base
+indentation. You can use this function to switch to using the
+indentation of the anchor as the base indentation."
+ (save-excursion
+ (goto-char (sqlind-anchor-point syntax))
+ (current-indentation)))
+
+(defun sqlind-lineup-to-anchor (syntax _base-indentation)
+ "Return the column of the anchor point of SYNTAX.
+This need not be the indentation of the actual line that contains
+anchor."
+ (save-excursion
+ (goto-char (sqlind-anchor-point syntax))
+ (current-column)))
+
+(defun sqlind-use-previous-line-indentation (syntax _base-indentation)
+ "Return the indentation of the previous line.
+If the start of the previous line is before the anchor of SYNTAX,
+use the column of the anchor + 1."
+ (let ((anchor (sqlind-anchor-point syntax)))
+ (save-excursion
+ (forward-line -1)
+ (back-to-indentation)
+ (if (< (point) anchor)
+ (progn
+ (goto-char anchor)
+ (1+ (current-column)))
+ (current-column)))))
+
+(defun sqlind-indent-comment-continuation (syntax _base-indentation)
+ "Return the indentation for a line inside a comment SYNTAX.
+If the current line matches `sqlind-comment-prefix' or
+`sqlind-comment-end', we indent to one plus the column of the
+comment start, which will make comments line up nicely, like
+this:
+
+ /* Some comment line
+ * another comment line
+ */
+
+When the current line does not match `sqlind-comment-prefix', we
+indent it so it lines up with the text of the start of the
+comment, like this:
+
+ /* Some comment line
+ Some other comment line
+ */"
+ (save-excursion
+ (back-to-indentation)
+ (if (or (looking-at sqlind-comment-prefix)
+ (looking-at sqlind-comment-end))
+ (progn
+ (goto-char (sqlind-anchor-point syntax))
+ (1+ (current-column)))
+ ;; else
+ (goto-char (sqlind-anchor-point syntax))
+ (when (looking-at sqlind-comment-start-skip)
+ (goto-char (match-end 0)))
+ (current-column))))
+
+(defun sqlind-indent-comment-start (syntax base-indentation)
+ "Return the indentation for a comment start SYNTAX.
+If we start a line comment (--) and the previous line also has a
+line comment, we line up the two comments. Otherwise we indent
+in the previous context (cdr SYNTAX) starting at
+BASE-INDENTATION."
+ (save-excursion
+ (back-to-indentation)
+ (if (and (looking-at "\\s *--")
+ (progn
+ (forward-line -1)
+ (re-search-forward "--" (line-end-position) t)))
+ (progn
+ (goto-char (match-beginning 0))
+ (current-column))
+ (sqlind-calculate-indentation
+ (sqlind-outer-context syntax) base-indentation))))
+
+(defun sqlind-indent-select-column (syntax base-indentation)
+ "Return the indentation for a column of a SELECT clause.
+
+SYNTAX is the syntax of the current line, BASE-INDENTATION is the
+current indentation, which we need to update.
+
+We try to align to the previous column start, but if we are the
+first column after the SELECT clause we simply add
+`sqlind-basic-offset'."
+ (save-excursion
+ (goto-char (sqlind-anchor-point syntax))
+ (when (looking-at "select\\s *\\(top\\s +[0-9]+\\|distinct\\|unique\\)?")
+ (goto-char (match-end 0)))
+ (skip-syntax-forward " ")
+ (if (or (looking-at sqlind-comment-start-skip)
+ (eolp))
+ (+ base-indentation sqlind-basic-offset)
+ (current-column))))
+
+(defun sqlind-indent-select-table (syntax base-indentation)
+ "Return the indentation for a table in the FROM section.
+
+SYNTAX is the syntax of the current line, BASE-INDENTATION is the
+current indentation, which we need to update.
+
+We try to align to the first table, but if we are the first
+table, we simply add `sqlind-basic-offset'."
+ (save-excursion
+ (goto-char (sqlind-anchor-point syntax))
+ (when (looking-at "from")
+ (goto-char (match-end 0)))
+ (skip-syntax-forward " ")
+ (if (or (looking-at sqlind-comment-start-skip)
+ (eolp))
+ (+ base-indentation sqlind-basic-offset)
+ (current-column))))
+
+(defun sqlind-lineup-to-clause-end (syntax base-indentation)
+ "Line up the current line with the end of a query clause.
+
+This assumes SYNTAX is one of in-select-clause, in-update-clause,
+in-insert-clause or in-delete-clause. It will return an
+indentation so that:
+
+If the clause is on a line by itself, the current line is
+indented by `sqlind-basic-offset', otherwise the current line is
+indented so that it starts in next column from where the clause
+keyword ends.
+Argument BASE-INDENTATION is updated."
+ (cl-destructuring-bind ((_sym clause) . anchor) (car syntax)
+ (save-excursion
+ (goto-char anchor)
+ (forward-char (1+ (length clause)))
+ (skip-syntax-forward " ")
+ (if (or (looking-at sqlind-comment-start-skip)
+ (eolp))
+ ;; if the clause is on a line by itself, indent this line with a
+ ;; sqlind-basic-offset
+ (+ base-indentation sqlind-basic-offset)
+ ;; otherwise, align to the end of the clause, with a few exceptions
+ (current-column)))))
+
+(defun sqlind-right-justify-logical-operator (syntax base-indentation)
+ "Align an AND, OR or NOT operator with the end of the WHERE clause.
+
+SYNTAX is the syntax of the current line, BASE-INDENTATION is the
+current indentation, which we need to update.
+
+If this rule is added to the 'in-select-clause syntax after the
+`sqlind-lineup-to-clause-end' rule, it will adjust lines starting
+with AND, OR or NOT to be aligned so they sit under the WHERE clause."
+ (save-excursion
+ (back-to-indentation)
+ (cl-destructuring-bind ((_sym clause) . anchor) (car syntax)
+ (if (and (equal clause "where")
+ (looking-at "and\\|or\\|not"))
+ (- base-indentation (1+ (- (match-end 0) (match-beginning 0))))
+ base-indentation))))
+
+(defun sqlind-left-justify-logical-operator (syntax base-indentation)
+ "Align an AND, OR or NOT operator with the start of the WHERE clause.
+
+SYNTAX is the syntax of the current line, BASE-INDENTATION is the
+current indentation, which we need to update.
+
+If this rule is added to the 'in-select-clause syntax after the
+`sqlind-lineup-to-clause-end' rule, it will adjust lines starting
+with AND, OR or NOT to be aligned so they sit left under the WHERE clause."
+ (save-excursion
+ (back-to-indentation)
+ (cl-destructuring-bind ((_sym clause) . anchor) (car syntax)
+ (if (and (equal clause "where")
+ (looking-at "and\\|or\\|not"))
+ (progn (goto-char anchor) (current-column))
+ base-indentation))))
+
+(defconst sqlind-operator-regexp
+ (concat (regexp-opt '("||" "*" "/" "%" "+" "-" "<<" ">>" "&" "|"
+ "<" "<=" ">" ">=" "=" "==" "!=" "<>")
+ t)
+ "\\s-*")
+ "Regexp to match a SQL expression operator.")
+
+(defun sqlind-adjust-operator (syntax base-indentation)
+ "Adjust the indentation for operators in select clauses.
+
+If the line to be indented starts with an operator, the
+right-hand operand is lined up with the left hand operand on the
+previous line. Otherwise, BASE-INDENTATION is returned. For
+example:
+
+ select col1, col2
+ || col3 as composed_column,
+ col4
+ || col5 as composed_column2
+ from my_table
+ where cond1 = 1
+ and cond2 = 2;
+
+This is an indentation adjuster and needs to be added to the
+`sqlind-indentation-offsets-alist`"
+ (save-excursion
+ (with-syntax-table sqlind-syntax-table
+ (back-to-indentation)
+ ;; If there are non-word constituents at the beginning if the line,
+ ;; consider them an operator and line up the line to the first word of the
+ ;; line, not the operator
+ (cond ((looking-at sqlind-operator-regexp)
+ (let ((ofs (length (match-string 0))))
+ (if (eq (sqlind-syntax-symbol syntax)
+ 'select-column-continuation)
+ (goto-char (sqlind-column-definition-start
+ (point) (sqlind-anchor-point syntax)))
+ (forward-line -1)
+ (end-of-line)
+ (sqlind-backward-syntactic-ws)
+ ;; Previous function leaves us on the first non-white-space
+ ;; character. This might be a string terminator (') char, move
+ ;; the cursor one to the left, so 'forward-sexp' works correctly.
+ (ignore-errors (forward-char 1))
+ (forward-sexp -1))
+ (max 0 (- (current-column) ofs))))
+ ('t base-indentation)))))
+
+(defun sqlind-lone-semicolon (syntax base-indentation)
+ "Indent a lone semicolon with the statement start. For example:
+
+If the current line contains a single semicolon ';', it will be
+indented at the same level as the anhcor of SYNTAX. Otherwise,
+BASE-INDENTATION is returned unchanged. For example:
+
+ select col
+ from my_table
+ ;
+
+This is an indentation adjuster and needs to be added to the
+`sqlind-indentation-offsets-alist`"
+ (save-excursion
+ (back-to-indentation)
+ (if (looking-at ";")
+ (sqlind-use-anchor-indentation syntax base-indentation)
+ base-indentation)))
+
+(defun sqlind-right-justify-clause (syntax base-indentation)
+ "Return an indentation which right-aligns the first word at
+anchor of SYNTAX with the first word in the curent line.
+
+This is intended to be used as an indentation function for
+select-clause, update-clause, insert-clause and update-clause
+syntaxes"
+ (save-excursion
+ (let ((clause-length 0)
+ (statement-keyword-length 0)
+ offset)
+ (back-to-indentation)
+ (when (looking-at "\\sw+\\b")
+ (setq clause-length (- (match-end 0) (match-beginning 0))))
+ (goto-char (cdar syntax)) ; move to ANCHOR
+ (when (looking-at "\\sw+\\b")
+ (setq statement-keyword-length (- (match-end 0) (match-beginning 0))))
+ (setq offset (- statement-keyword-length clause-length))
+ (if (> offset 0)
+ (+ base-indentation offset)
+ base-indentation))))
+
+(defun sqlind-lineup-joins-to-anchor (syntax base-indentation)
+ "Align JOIN keywords with the anchor point of SYNTAX.
+If the line starts with an INNER,OUTER or CROSS JOIN keyword,
+return the column of the anchor point, otherwise return
+BASE-INDENTATION.
+
+If this rule is added to `select-table-continuation' indentation,
+it will indent lines starting with JOIN keywords to line up with
+the FROM keyword."
+ (save-excursion
+ (back-to-indentation)
+ ;; 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)))
+
+(defun sqlind-lineup-open-paren-to-anchor (syntax base-indentation)
+ "Align an open paranthesis with the anchor point of SYNTAX.
+If the line starts with an open paren '(', return the column of
+the anchor point. If line does not start with an open paren, the
+function returns BASE-INDENTATION, acting as a no-op."
+ (save-excursion
+ (back-to-indentation)
+ (if (looking-at "(")
+ (sqlind-lineup-to-anchor syntax base-indentation)
+ base-indentation)))
+
+(defun sqlind-adjust-comma (_syntax base-indentation)
+ "Lineup lines starting with a comma ',' to the word start.
+Adjust BASE-INDENTATION so that the actual word is lined up. For
+example:
+
+ SELECT col1
+ , col2 -- ignore the comma and align the actual word."
+ (save-excursion
+ (back-to-indentation)
+ (let ((ofs (if (looking-at ",\\s-*") (length (match-string 0)) 0)))
+ (max 0 (- base-indentation ofs)))))
+
+(defun sqlind-lineup-into-nested-statement (syntax _base-indentation)
+ "Align the line to the first word inside a nested statement.
+Return the column of the first non-witespace char in a nested
+statement. For example:
+
+ ( a,
+ b, -- b is always aligned with 'a'
+ )
+
+This function only makes sense in a
+'nested-statement-continuation SYTNAX indentation rule."
+ (save-excursion
+ (goto-char (sqlind-anchor-point syntax))
+ (forward-char 1)
+ (sqlind-forward-syntactic-ws)
+ (current-column)))
+
+;;;;; sqlind-indent-line
+
+(defun sqlind-indent-line ()
+ "Indent the current line according to SQL conventions.
+`sqlind-basic-offset' defined the number of spaces in the basic
+indentation unit and `sqlind-indentation-offsets-alist' is used to
+determine how to indent each type of syntactic element."
+ (let* ((syntax (sqlind-syntax-of-line))
+ (base-column (current-column))
+ (indent-column (sqlind-calculate-indentation syntax)))
+ (when indent-column
+ (back-to-indentation)
+ (let ((offset (- base-column (current-column))))
+ ;; avoid modifying the buffer when the indentation does not
+ ;; have to change
+ (unless (eq (current-column) indent-column)
+ (indent-line-to indent-column))
+ (when (> offset 0)
+ (forward-char offset))))))
+
+;;;; Alignment rules
+
+(defvar sqlind-align-rules
+ `(;; Line up the two side of arrow =>
+ (sql-arrow-lineup
+ (regexp . "\\(\\s-*\\)=>\\(\\s-*\\)")
+ (modes quote (sql-mode))
+ (group 1 2)
+ (case-fold . t)
+ (repeat . t))
+ ;; Line up the two sides of an assigment
+ (sql-assign-lineup
+ (regexp . "^\\s-*\\S-+\\(\\s-*\\):=\\(\\s-*\\)")
+ (modes quote (sql-mode))
+ (group 1 2)
+ (case-fold . t)
+ (repeat . t))
+ ;; lineup a variable declaration with an assignment
+ (sql-assign-lineup-2
+ (regexp . "^\\s-*\\S-+\\(\\s-+\\)\\S-+\\(\\s-*\\):=\\(\\s-*\\)")
+ (modes quote (sql-mode))
+ (group 1 2 3)
+ (case-fold . t)
+ (repeat . t))
+ ;; Line up the two sides of in / out / in out parameter
+ (sql-param-lineup-in-out
+ (regexp . "\\(\\s-+\\)\\(in out\\|in\\|out\\)\\(\\s-+\\)")
+ (modes quote (sql-mode))
+ (group 1 3)
+ (case-fold . t)
+ (repeat . t))
+ ;; Line up the two sides of an equal sign in an update expression
+ (sql-equals
+ (regexp . "[^:<>!]\\(\\s-*\\)=\\(\\s-*\\)[^>]")
+ (modes quote (sql-mode))
+ (group 1 2)
+ (case-fold . t)
+ (valid . ,(function (lambda ()
+ (save-excursion
+ (goto-char (match-end 1))
+ (not (nth 3 (syntax-ppss (point))))))))
+ (repeat . t))
+ (sql-comparison-operators ; see issue #47
+ (regexp . "[^:]\\(\\s-*\\)[<>=!]+\\(\\s-*\\)[^>]")
+ (modes quote (sql-mode))
+ (group 1 2)
+ (case-fold . t)
+ (valid . ,(function (lambda ()
+ (save-excursion
+ (goto-char (match-end 1))
+ (not (nth 3 (syntax-ppss (point))))))))
+ (repeat . t))
+ ;; Line up the two sides of piped string
+ (sql-pipes
+ (regexp . "[^:]\\(\\s-*\\)||\\(\\s-*\\)[^>]")
+ (modes quote (sql-mode))
+ (group 1 2)
+ (case-fold . t)
+ (repeat . t))
+ ;; lineup the column aliases (the "as name" part) in a select statement
+ (sql-select-lineup-column-names
+ (regexp . ".*?\\(\\s +\\)as\\(\\s +\\).*")
+ (modes quote (sql-mode))
+ (group 1 2)
+ (case-fold . t)
+ (repeat . t)))
+ "Align rules for SQL codes.
+
+These rules help aligning some SQL statements, such as the column
+names in select queries, or equal signs in update statements or
+where clauses.
+
+To use it, select the region to be aligned and run \\[align].
+
+See also `align' and `align-rules-list'")
+
+;;;; sqlind-minor-mode, sqlind-setup
+
+(defvar sqlind-minor-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map [remap beginning-of-defun] 'sqlind-beginning-of-block)
+ map))
+
+(defvar align-mode-rules-list)
+
+;;;###autoload
+(define-minor-mode sqlind-minor-mode
+ "Toggle SQL syntactic indentation on or off.
+With syntactic indentation, hitting TAB on a line in a SQL buffer
+will indent the line according to the syntactic context of the
+SQL statement being edited.
+
+A set of alignment rules are also enabled with this minor mode.
+Selecting a region of text and typing `M-x align RET` will align
+the statements. This can be used, for example, to align the 'as'
+column aliases in select statements."
+ :lighter " sqlind"
+ ;; :group 'sqlind ;;FIXME: There's no such group!
+ :global nil
+ :init-value nil
+ (if sqlind-minor-mode
+ (progn
+ (set (make-local-variable 'indent-line-function) #'sqlind-indent-line)
+ (set (make-local-variable 'align-mode-rules-list) sqlind-align-rules))
+ (progn
+ (kill-local-variable 'indent-line-function)
+ (kill-local-variable 'align-mode-rules-list))))
+
+;;;###autoload
+(defun sqlind-setup ()
+ "Enable SQL syntactic indentation unconditionally.
+This function is deprecated, consider using the function
+`sqlind-minor-mode' instead."
+ (set (make-local-variable 'indent-line-function) 'sqlind-indent-line)
+ (define-key sql-mode-map [remap beginning-of-defun] 'sqlind-beginning-of-statement)
+ (setq align-mode-rules-list sqlind-align-rules))
+
+;; Local Variables:
+;; mode: emacs-lisp
+;; mode: outline-minor
+;; outline-regexp: ";;;;+"
+;; End:
+
+(provide 'sql-indent)
+;;; sql-indent.el ends here
--- /dev/null
+
+ sql-indent.el -- syntax based indentation for SQL files for GNU Emacs
+
+*NOTE* This file is formatted as an Emacs Org file. If you open it in GNU
+Emacs, you will be able to open-close sections of the file, which will make
+navigation easier.
+
+* Overview
+
+sql-indent.el is a GNU Emacs minor mode which adds support for syntax-based
+indentation when editing SQL code: TAB indents the current line based on the
+syntax of the SQL code on previous lines. This works like the indentation for
+C and C++ code.
+
+The package also defines align rules so that the ~align~ function works for
+SQL statements, see ~sqlind-align-rules~ for which rules are defined. This
+can be used to align multiple lines around equal signs or "as" statements.
+Here is an example of alignment rules:
+
+#+BEGIN_SRC sql
+ update my_table
+ set col1_has_a_long_name = value1,
+ col2_is_short = value2,
+ col3_med = v2,
+ c4 = v5
+ where cond1 is not null;
+
+ select long_colum as lcol,
+ scol as short_column,
+ mcolu as mcol,
+ from my_table;
+#+END_SRC
+
+To use this feature, select the region you want to align and type:
+
+#+BEGIN_SRC text
+ M-x align RET
+#+END_SRC
+
+~sqlind-minor-mode~ together with the align rules can assist in writing tidy
+SQL code or formatting existing SQL code. The indentation rules are
+customizable, so you can adapt it to match your own indentation preferences.
+
+* Installation
+
+To install this package, open the file ~sql-indent.el~ in Emacs and type
+
+#+BEGIN_SRC text
+ M-x package-install-from-buffer RET
+#+END_SRC
+
+The syntax-based indentation of SQL code can be turned ON/OFF at any time by
+enabling or disabling ~sqlind-minor-mode~:
+
+#+BEGIN_SRC text
+ M-x sqlind-minor-mode RET
+#+END_SRC
+
+To enable syntax-based indentation for every SQL buffer, you can add
+~sqlind-minor-mode~ to ~sql-mode-hook~. First, bring up the customization
+buffer using the command:
+
+#+BEGIN_SRC text
+ M-x customize-variable RET sql-mode-hook RET
+#+END_SRC
+
+Than, click on the "INS" button to add a new entry and put "sqlind-minor-mode"
+in the text field.
+
+* Customization
+
+The sql-indent.el package allows customizing the indentation rules to suit
+your personal preferences or site specific coding styles. To create a set of
+customized rules, you will need to have basic familiarity with how indentation
+works. See also the "A Simple Example" section below and the
+~sql-indent-left.el~ file, which demonstrate how to set up custom indentation
+rules. The sections below contain the rest of the details.
+
+The indentation process happens in two separate phases: first syntactic
+information is determined about the line, than the line is indented based on
+that syntactic information. The syntactic parse is not expected to change
+often, since it deals with the structure of the SQL code, however, indentation
+is a personal preference, and can be easily customized.
+
+Two variables control the way code is indented: ~sqlind-basic-offset~ and
+~sqlind-indentation-offsets-alist~. To customize these variables, you need to
+create a function that sets custom values and add it to ~sql-mode-hook~.
+
+** A Simple Example
+
+The default indentation rules will align to the right all the keywords in a
+SELECT statement, like this:
+
+#+BEGIN_SRC sql
+ select c1, c2
+ from t1
+ where c3 = 2
+#+END_SRC
+
+If you prefer to have them aligned to the left, like this:
+
+#+BEGIN_SRC sql
+ select c1, c2
+ from t1
+ where c3 = 2
+#+END_SRC
+
+You can add the following code to your init file:
+
+#+BEGIN_SRC emacs-lisp
+ (require 'sql-indent)
+
+ ;; Update indentation rules, select, insert, delete and update keywords
+ ;; are aligned with the clause start
+
+ (defvar my-sql-indentation-offsets-alist
+ `((select-clause 0)
+ (insert-clause 0)
+ (delete-clause 0)
+ (update-clause 0)
+ ,@sqlind-default-indentation-offsets-alist))
+
+ (add-hook 'sqlind-minor-mode-hook
+ (lambda ()
+ (setq sqlind-indentation-offsets-alist
+ my-sql-indentation-offsets-alist)))
+#+END_SRC
+
+** Customization Basics
+
+To customize indentation, you will need to provide a new value for the
+~sqlind-indentation-offsets-alist~ variable. The variable is made buffer
+local each time it is set, so you need to set it inside the ~sql-mode-hook~.
+The variable specifies how each syntactic symbol should be indented. Since
+only a few symbols need to be updated, the usual way to update it is to
+"extend" the value of ~sqlind-default-indentation-offsets-alist~, like so:
+
+#+BEGIN_SRC emacs-lisp
+ (defvar my-sql-indentation-offsets-alist
+ `( ;; put new syntactic symbols here, and add the default ones at the end.
+ ;; If there is no value specified for a syntactic symbol, the default
+ ;; will be picked up.
+ ,@sqlind-default-indentation-offsets-alist))
+
+ ;; Arrange for the new indentation offset to be set up for each SQL buffer.
+ (add-hook 'sqlind-minor-mode-hook
+ (lambda ()
+ (setq sqlind-indentation-offsets-alist
+ my-sql-indentation-offsets-alist)))
+#+END_SRC
+
+The simplest way to adjust the indentation is to explore the syntactic
+information using ~sqlind-show-syntax-of-line~. To use it, move the cursor to
+the line you would like to indent and type:
+
+#+BEGIN_SRC text
+M-x sqlind-show-syntax-of-line RET
+#+END_SRC
+
+A message like the one below will be shown in the messages buffer:
+
+#+BEGIN_SRC text
+((select-clause . 743) (statement-continuation . 743))
+#+END_SRC
+
+The first symbol displayed is the syntactic symbol used for indentation, in
+this case ~select-clause~. The syntactic symbols are described in a section
+below, however, for now, this is the symbol that will need to be updated in
+~sqlind-indentation-offsets-alist~. The number next to it represents the
+anchor, or reference position in the buffer where the current statement
+starts. The anchor and is useful if you need to write your own indentation
+functions.
+
+To customize indentation for this type of statement, add an entry in the
+~sqlind-indentation-offsets-alist~, for the syntactic symbol shown, with
+information about how it should be indented. This information is a list
+containing *indentation control items* (these are described below).
+
+For example, to indent keyword in SELECT clauses at the same level as the
+keyword itself, we use a number which is added to the indentation level of the
+anchor, in this case, 0:
+
+#+BEGIN_SRC text
+(select-clause 0)
+#+END_SRC
+
+To indent it at ~sqlind-basic-offset~ plus one more space, use:
+
+#+BEGIN_SRC text
+(select-clause + 1)
+#+END_SRC
+
+To right-justify the keyword w.r.t the SELECT keyword, use:
+
+#+BEGIN_SRC text
+(select-clause sqlind-right-justify-clause)
+#+END_SRC
+
+The default value for ~sqlind-indentation-offsets-alist~ contains many
+examples for indentation setup rules.
+
+** Indentation control items
+
+~sqlind-calculate-indentation~ is the function that calculates the indentation
+offset to use, based on the contents of ~sqlind-indentation-offsets-alist~.
+The indentation offset starts with the indentation column of the ANCHOR point
+and it is adjusted based on the following items:
+
+ * a ~NUMBER~ -- the NUMBER will be added to the indentation offset.
+
+ * ~+~ -- the current indentation offset is incremented by
+ ~sqlind-basic-offset~
+
+ * ~++~ -- the current indentation offset is indentation by ~2 *
+ sqlind-basic-offset~
+
+ * ~-~ -- the current indentation offset is decremented by
+ ~sqlind-basic-offset~
+
+ * ~--~ -- the current indentation offset is decremented by 2 *
+ ~sqlind-basic-offset~
+
+ * a ~FUNCTION~ -- the syntax and current indentation offset is passed to the
+ function and its result is used as the new indentation offset. This can be
+ used to further customize indentation.
+
+*** Indentation Helper Functions
+
+The following helper functions are available as part of the package and can be
+used as the FUNCTION part in the ~sqlind-indentation-offsets-alist~
+
+**** sqlind-use-anchor-indentation
+
+discard the current offset and returns the indentation column of the ANCHOR
+
+**** sqlind-lineup-to-anchor
+
+discard the current offset and returns the column of the anchor point, which
+may be different than the indentation column of the anchor point.
+
+**** sqlind-use-previous-line-indentation
+
+discard the current offset and returns the indentation column of the previous
+line
+
+**** sqlind-lineup-open-paren-to-anchor
+
+if the line starts with an open parenthesis, discard the current
+offset and return the column of the anchor point.
+
+**** sqlind-lone-semicolon
+
+if the line contains a single semicolon ';', use the value of
+~sqlind-use-anchor-indentation~
+
+**** sqlind-adjust-operator
+
+if the line starts with an arithmetic operator (like ~+~ , ~-~, or ~||~), line
+it up so that the right hand operand lines up with the left hand operand of
+the previous line. For example, it will indent the ~||~ operator like this:
+
+#+BEGIN_SRC sql
+select col1, col2
+ || col3 as composed_column, -- align col3 with col2
+ col4
+ || col5 as composed_column2
+from my_table
+where cond1 = 1
+and cond2 = 2;
+#+END_SRC
+
+**** sqlind-left-justify-logical-operator
+
+If the line starts with a logic operator (AND, OR NOT), line the operator with
+the start of the WHERE clause. This rule should be added to the
+~in-select-clause~ syntax after the ~sqlind-lineup-to-clause-end~ rule.
+
+**** sqlind-right-justify-logical-operator
+
+If the line starts with a logic operator (AND, OR NOT), line the operator with
+the end of the WHERE clause. This rule should be added to the
+~in-select-clause~ syntax.
+
+#+BEGIN_SRC sql
+select *
+ from table
+ where a = b
+ and c = d; -- AND clause sits under the where clause
+#+END_SRC
+
+**** sqlind-adjust-comma
+
+if the line starts with a comma, adjust the current offset so that the line is
+indented to the first word character. For example, if added to a
+~select-column~ syntax indentation rule, it will indent as follows:
+
+#+BEGIN_SRC sql
+select col1
+ , col2 -- align "col2" with "col1"
+from my_table;
+#+END_SRC
+
+**** sqlind-lineup-into-nested-statement
+
+discard the current offset and return the column of the first word inside a
+nested statement. This rule should be added to
+~nested-statement-continuation~ syntax indentation rule, and will indent as
+follows:
+
+#+BEGIN_SRC sql
+( a,
+ b -- b is aligned with a
+)
+#+END_SRC
+
+*** More Indentation Helper Functions
+The following function contain indentation code specific to various SQL
+statements. Have a look at their doc strings for what they do:
+
+ * ~sqlind-indent-comment-start~, ~sqlind-indent-comment-continuation~
+
+ * ~sqlind-indent-select-column~
+
+ * ~sqlind-indent-select-table~
+
+ * ~sqlind-lineup-to-clause-end~
+
+ * ~sqlind-right-justify-clause~
+
+ * ~sqlind-lineup-joins-to-anchor~
+
+** Syntactic Symbols
+
+The SQL parsing code returns a syntax definition (either a symbol or a
+list) and an anchor point, which is a buffer position. The syntax symbols can
+be used to define how to indent each line. The following syntax symbols are
+defined for SQL code:
+
+ * ~(syntax-error MESSAGE START END)~ -- this is returned when the parse
+ failed. MESSAGE is an informative message, START and END are buffer
+ locations denoting the problematic region. ANCHOR is undefined for this
+ syntax info
+
+ * ~in-comment~ -- line is inside a multi line comment, ANCHOR is the start of
+ the comment.
+
+ * ~comment-start~ -- line starts with a comment. ANCHOR is the start of the
+ enclosing block.
+
+ * ~in-string~ -- line is inside a string, ANCHOR denotes the start of the
+ string.
+
+ * ~toplevel~ -- line is at toplevel (not inside any programming construct).
+ ANCHOR is usually (point-min).
+
+ * ~(in-block BLOCK-KIND LABEL)~ -- line is inside a block construct.
+ BLOCK-KIND (a symbol) is the actual block type and can be one of "if",
+ "case", "exception", "loop" etc. If the block is labeled, LABEL contains
+ the label. ANCHOR is the start of the block.
+
+ * ~(in-begin-block KIND LABEL)~ -- line is inside a block started by a begin
+ statement. KIND (a symbol) is "toplevel-block" for a begin at toplevel,
+ "defun" for a begin that starts the body of a procedure or function,
+ \"package\" for a begin that starts the body of a package, nil for a begin
+ that is none of the previous. For a "defun" or "package", LABEL is the
+ name of the procedure, function or package, for the other block types LABEL
+ contains the block label, or the empty string if the block has no label.
+ ANCHOR is the start of the block.
+
+ * ~(block-start KIND)~ -- line begins with a statement that starts a block.
+ KIND (a symbol) can be one of "then", "else" or "loop". ANCHOR is the
+ reference point for the block start (the corresponding if, case, etc).
+
+ * ~(block-end KIND LABEL)~ -- the line contains an end statement. KIND (a
+ symbol) is the type of block we are closing, LABEL (a string) is the block
+ label (or procedure name for an end defun).
+
+ * ~declare-statement~ -- line is after a declare keyword, but before the
+ begin. ANCHOR is the start of the declare statement.
+
+ * ~(package NAME)~ -- line is inside a package definition. NAME is the name
+ of the package, ANCHOR is the start of the package.
+
+ * ~(package-body NAME)~ -- line is inside a package body. NAME is the name
+ of the package, ANCHOR is the start of the package body.
+
+ * ~(create-statement WHAT NAME)~ -- line is inside a CREATE statement (other
+ than create procedure or function). WHAT is the thing being created, NAME
+ is its name. ANCHOR is the start of the create statement.
+
+ * ~(defun-start NAME)~ -- line is inside a procedure of function definition
+ but before the begin block that starts the body. NAME is the name of the
+ procedure/function, ANCHOR is the start of the procedure/function
+ definition.
+
+The following SYNTAX-es are for SQL statements. For all of them ANCHOR points
+to the start of a statement itself.
+
+ * ~labeled-statement-start~ -- line is just after a label.
+
+ * ~statement-continuation~ -- line is inside a statement which starts on a
+ previous line.
+
+ * ~nested-statement-open~ -- line is just inside an opening bracket, but the
+ actual bracket is on a previous line.
+
+ * ~nested-statement-continuation~ -- line is inside an opening bracket, but
+ not the first element after the bracket.
+
+ * ~nested-statement-close~ line is inside an opening bracket and the line
+ contains the closing bracket as the first character.
+
+The following SYNTAX-es are for statements which are SQL code (DML
+statements). They are specializations on the previous statement syntaxes and
+for all of them a previous generic statement syntax is present earlier in the
+SYNTAX list. Unless otherwise specified, ANCHOR points to the start of the
+clause (select, from, where, etc) in which the current point is.
+
+ * ~with-clause~ -- line is inside a WITH clause, but before the main SELECT
+ clause.
+
+ * ~with-clause-cte~ -- line is inside a with clause before a CTE (common
+ table expression) declaration
+
+ * ~with-clause-cte-cont~ -- line is inside a with clause before a CTE
+ definition
+
+ * ~case-clause~ -- line is on a CASE expression (WHEN or END clauses).
+ ANCHOR is the start of the CASE expression.
+
+ * ~case-clause-item~ -- line is on a CASE expression (THEN and ELSE clauses).
+ ANCHOR is the position of the case clause.
+
+ * ~case-clause-item-cont~ -- line is on a CASE expression but not on one of
+ the CASE sub-keywords. ANCHOR points to the case keyword that this line is
+ a continuation of.
+
+ * ~select-clause~ -- line is inside a select statement, right before one of
+ its clauses (from, where, order by, etc).
+
+ * ~select-column~ -- line is inside the select column section, after a full
+ column was defined (and a new column definition is about to start).
+
+ * ~select-column-continuation~ -- line is inside the select column section,
+ but in the middle of a column definition. The defined column starts on a
+ previous like. Note that ANCHOR still points to the start of the select
+ statement itself.
+
+ * ~select-join-condition~ -- line is right before or just after the ON clause
+ for an INNER, LEFT or RIGHT join. ANCHOR points to the join statement for
+ which the ON is defined.
+
+ * ~select-table~ -- line is inside the from clause, just after a table was
+ defined and a new one is about to start.
+
+ * ~select-table-continuation~ -- line is inside the from clause, inside a
+ table definition which starts on a previous line. Note that ANCHOR still
+ points to the start of the select statement itself.
+
+ * ~(in-select-clause CLAUSE)~ -- line is inside the select CLAUSE, which can
+ be "where", "order by", "group by" or "having". Note that CLAUSE can never
+ be "select" and "from", because we have special syntaxes inside those
+ clauses.
+
+ * ~insert-clause~ -- line is inside an insert statement, right before one of
+ its clauses (values, select).
+
+ * ~(in-insert-clause CLAUSE)~ -- line is inside the insert CLAUSE, which can
+ be "insert into" or "values".
+
+ * ~delete-clause~ -- line is inside a delete statement right before one of
+ its clauses.
+
+ * ~(in-delete-clause CLAUSE)~ -- line is inside a delete CLAUSE, which can be
+ "delete from" or "where".
+
+ * ~update-clause~ -- line is inside an update statement right before one of
+ its clauses.
+
+ * ~(in-update-clause CLAUSE)~ -- line is inside an update CLAUSE, which can
+ be "update", "set" or "where"
+* Limitations
+** Parsing expressions
+
+There is no support for parsing SQL expressions, so if an expression is broken
+over several lines, sql-indent.el will consider all lines to be
+~statement-continuation~ lines. The exception is that bracketed expressions
+are identified correctly so they can be used for indentation.
+
+The examples below summarize what is supported and what is not, as well as the
+workarounds:
+
+#+BEGIN_SRC sql
+ -- SUPPORTED: case expression immediately after assignment
+ var := case ind
+ when 1 then 'Guy'
+ when 2 then 'Abc'
+ when 3 then 'Def'
+ else 'World'
+ end case;
+
+ -- NOT SUPPORTED: any complex expression involving a case expression. entire
+ -- expression is a 'statement-continuation
+ var := 'abc'
+ || case ind
+ when 1 then 'Guy'
+ when 2 then 'Abc'
+ when 3 then 'Def'
+ else 'World'
+ end case;
+
+ -- WORKAROUND: use brackets instead
+ var := 'abc'
+ || (case ind
+ when 1 then 'Guy'
+ when 2 then 'Abc'
+ when 3 then 'Def'
+ else 'World'
+ end case);
+
+ -- SUPPORTED: case expression as select column
+ select col1,
+ case ind
+ when 1 then 'Guy'
+ when 2 then 'Abc'
+ when 3 then 'Def'
+ else 'World'
+ end case,
+ col2,
+ from some_table;
+
+ -- NOT SUPPORTED: any complex expression involving a case expression in a
+ -- select column. Entire column is a 'select-column-continuation
+ select col1,
+ 'abc' || case ind
+ when 1 then 'Guy'
+ when 2 then 'Abc'
+ when 3 then 'Def'
+ else 'World'
+ end case,
+ col2,
+ from some_table;
+
+ -- WORKAROUND: use brackets instead
+ select col1,
+ 'abc' || (case ind
+ when 1 then 'Guy'
+ when 2 then 'Abc'
+ when 3 then 'Def'
+ else 'World'
+ end case),
+ col2,
+ from some_table;
+#+END_SRC
+
+.
--- /dev/null
+(((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1))
+ ((select-column . 52)
+ (statement-continuation . 52))
+ ((case-clause . 70)
+ (select-column-continuation . 52)
+ (statement-continuation . 52))
+ ((case-clause . 70)
+ (select-column-continuation . 52)
+ (statement-continuation . 52))
+ ((case-clause . 70)
+ (select-column-continuation . 52)
+ (statement-continuation . 52))
+ (((block-end case "")
+ . 70)
+ (select-column-continuation . 52)
+ (statement-continuation . 52))
+ ((select-clause . 52)
+ (statement-continuation . 52))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((case-clause-item-cont . 197)
+ (statement-continuation . 190))
+ ((case-clause . 197)
+ (statement-continuation . 190))
+ ((case-clause . 197)
+ (statement-continuation . 190))
+ ((case-clause . 197)
+ (statement-continuation . 190))
+ (((block-end case "")
+ . 197)
+ (statement-continuation . 190))
+ ((toplevel . 1)))
+
--- /dev/null
+-- https://github.com/bsvingen/sql-indent/issues/1
+SELECT y ,
+ CASE
+ WHEN foo>5 THEN "great"
+ WHEN foo=5 THEN "normal"
+ ELSE "poor"
+ END AS level
+ FROM bar;
+
+var := CASE
+ var -- tricky case
+ WHEN foo>5 THEN "great"
+ WHEN foo=5 THEN "normal"
+ ELSE "poor"
+ END;
--- /dev/null
+(((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((create-statement index "ix1_some_table")
+ . 39))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((create-statement index "ix2_some_table")
+ . 98))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((create-statement index "ix3_some_table")
+ . 177))
+ ((toplevel . 1)))
--- /dev/null
+drop index if exists IX1_SOME_TABLE;
+
+create index IX1_SOME_TABLE
+ on SOME_TABLE(some_column);
+
+create index if not exists IX2_SOME_TABLE
+ on SOME_TABLE(some_other_column);
+
+create index IX3_SOME_TABLE
+ on SOME_TABLE(some_other_column);
--- /dev/null
+(0 4 4 6 6 4 0 4 4 4 4 4 4 4 10 4 4 4 8 4 4 0 0 10 16 16 16 16 20 20 20 16 12 16 10 1 5 0 0 0 6 6 2 6 0 8 0 4 0)
--- /dev/null
+(((toplevel . 1))
+ (((in-insert-clause "insert into")
+ . 1)
+ (statement-continuation . 1))
+ (((in-insert-clause "insert into")
+ . 1)
+ (statement-continuation . 1))
+ ((nested-statement-continuation . 25)
+ (statement-continuation . 25))
+ ((nested-statement-continuation . 25)
+ (statement-continuation . 25))
+ ((nested-statement-close . 25)
+ (statement-continuation . 25))
+ ((insert-clause . 1)
+ (statement-continuation . 1))
+ ((select-column . 80)
+ (statement-continuation . 1))
+ ((select-column . 80)
+ (statement-continuation . 1))
+ ((select-column . 80)
+ (statement-continuation . 1))
+ ((select-column . 80)
+ (statement-continuation . 1))
+ ((select-column . 80)
+ (statement-continuation . 1))
+ ((select-column . 80)
+ (statement-continuation . 1))
+ ((select-column-continuation . 80)
+ (statement-continuation . 1))
+ ((nested-statement-continuation . 242)
+ (statement-continuation . 242))
+ ((nested-statement-close . 242)
+ (statement-continuation . 242))
+ ((select-column . 80)
+ (statement-continuation . 1))
+ ((select-column-continuation . 80)
+ (statement-continuation . 1))
+ ((nested-statement-continuation . 327)
+ (statement-continuation . 327))
+ ((nested-statement-close . 327)
+ (statement-continuation . 327))
+ ((select-column . 80)
+ (statement-continuation . 1))
+ ((select-clause . 80)
+ (statement-continuation . 1))
+ ((select-table-continuation . 411)
+ (statement-continuation . 1))
+ ((select-table-continuation . 433)
+ (nested-statement-continuation . 423)
+ (statement-continuation . 423))
+ ((select-column . 461)
+ (nested-statement-continuation . 459)
+ (statement-continuation . 459))
+ ((select-column . 461)
+ (nested-statement-continuation . 459)
+ (statement-continuation . 459))
+ ((select-column . 461)
+ (nested-statement-continuation . 459)
+ (statement-continuation . 459))
+ ((select-column-continuation . 461)
+ (nested-statement-continuation . 459)
+ (statement-continuation . 459))
+ ((nested-statement-continuation . 614)
+ (statement-continuation . 614))
+ ((nested-statement-continuation . 614)
+ (statement-continuation . 614))
+ ((nested-statement-continuation . 614)
+ (statement-continuation . 614))
+ ((nested-statement-close . 614)
+ (statement-continuation . 614))
+ ((select-clause . 461)
+ (nested-statement-continuation . 459)
+ (statement-continuation . 459))
+ ((select-table-continuation . 795)
+ (nested-statement-continuation . 459)
+ (statement-continuation . 459))
+ ((nested-statement-close . 459)
+ (statement-continuation . 459))
+ ((select-clause . 424)
+ (nested-statement-continuation . 423)
+ (statement-continuation . 423))
+ (((in-select-clause "where")
+ . 865)
+ (nested-statement-continuation . 423)
+ (statement-continuation . 423))
+ ((nested-statement-close . 423)
+ (statement-continuation . 423))
+ ((select-table-continuation . 411)
+ (statement-continuation . 1))
+ ((select-table-continuation . 411)
+ (statement-continuation . 1))
+ ((select-column . 928)
+ (nested-statement-continuation . 926)
+ (statement-continuation . 926))
+ ((select-column . 928)
+ (nested-statement-continuation . 926)
+ (statement-continuation . 926))
+ ((select-clause . 928)
+ (nested-statement-continuation . 926)
+ (statement-continuation . 926))
+ ((select-table-continuation . 990)
+ (nested-statement-continuation . 926)
+ (statement-continuation . 926))
+ ((nested-statement-close . 926)
+ (statement-continuation . 926))
+ ((select-join-condition . 908)
+ (statement-continuation . 1))
+ ((select-clause . 80)
+ (statement-continuation . 1))
+ (((in-select-clause "group by")
+ . 1051)
+ (statement-continuation . 1))
+ (((in-select-clause "group by")
+ . 1051)
+ (statement-continuation . 1)))
--- /dev/null
+INSERT INTO
+ xyzxx
+ ( aaa, bbb, ccc
+ , ddd, eee, fff, ggg
+ , hhh
+ )
+SELECT
+ aaa
+ , MAX (m.b1) AS bbb
+ , MIN (m.b1) AS ccc
+ , COALESCE (MAX (n.c2), 0)  AS ddd
+ , COALESCE (MIN (n.c2), 0)  AS eee
+ , MAX (m.b1) OVER
+ ( PARTITION BY c2
+ ORDER BY aaa DESC
+ ) AS fff
+ , MIN (m.b1) OVER
+ ( PARTITION BY c2
+ ORDER BY aaa DESC
+ ) AS ggg
+ , AVG (n.c2)  AS hhh
+ FROM
+ (SELECT * FROM
+ ( SELECT
+ aaa
+ , jjj + kkk  AS b1
+ , ROW_NUMBER () OVER
+ ( PARTITION BY qqq
+ ORDER BY
+ rrr
+ , sss
+ ) AS rn
+ FROM
+ mno
+ )
+ WHERE
+ rn = 1
+ )  m
+ INNER JOIN
+ ( SELECT
+ aaa
+ , nnn + ooo AS c2
+ FROM
+ pqr
+ ) n
+ USING (aaa),
+ GROUP BY
+ aaa
--- /dev/null
+(0 0 0 0 0 0 0 0 9 9 9 7 9 2 1 8 4 0 0 7 7 7 7 7 7 2 9 1 4 4 4 4 4 4 25 32 24 27 27 27 27 27 8 0 0 1 8 15 8 8 0 0 3 10 1 7 13 7 7 0 0 12 12 12 0 7 7 7 7 7 7 24 7 24 7 2 31 31 51 51 26 10 10 29 24 14 1 10 1 10 10 0 0 0 0)
--- /dev/null
+(0 0 0 0 0 0 0 0 10 10 10 7 4 0 0 12 0 0 0 7 7 7 7 7 7 0 7 0 0 0 0 0 0 0 19 26 19 19 19 19 19 19 0 0 0 0 0 7 0 0 0 0 0 7 0 0 6 0 0 0 0 2 3 3 0 7 7 7 7 7 7 24 7 24 7 0 29 29 49 49 22 7 2 21 14 6 0 9 0 9 0 0 0 0 0)
--- /dev/null
+(0 0 0 0 0 0 0 0 10 10 10 7 4 2 1 13 4 0 0 7 7 7 7 7 7 2 9 1 4 4 4 4 4 4 25 32 24 27 27 27 27 27 1 0 0 1 4 11 4 1 0 0 3 10 1 3 9 3 1 0 0 12 12 12 0 7 7 7 7 7 7 24 7 24 7 2 31 31 51 51 26 10 10 29 24 14 1 10 1 10 1 0 0 0 0)
\ No newline at end of file
--- /dev/null
+(((toplevel . 1))
+ ((toplevel . 15))
+ ((comment-start . 33)
+ (toplevel . 33))
+ ((comment-start . 33)
+ (toplevel . 33))
+ ((toplevel . 33))
+ ((toplevel . 33))
+ ((toplevel . 33))
+ ((toplevel . 33))
+ ((select-column-continuation . 99)
+ (statement-continuation . 99))
+ ((select-column-continuation . 99)
+ (statement-continuation . 99))
+ ((select-column-continuation . 99)
+ (statement-continuation . 99))
+ ((select-column . 99)
+ (statement-continuation . 99))
+ ((select-column-continuation . 99)
+ (statement-continuation . 99))
+ ((select-clause . 99)
+ (statement-continuation . 99))
+ ((select-clause . 99)
+ (statement-continuation . 99))
+ (((in-select-clause "where")
+ . 306)
+ (statement-continuation . 99))
+ (((in-select-clause "where")
+ . 306)
+ (statement-continuation . 99))
+ ((toplevel . 33))
+ ((toplevel . 33))
+ ((select-column . 375)
+ (statement-continuation . 375))
+ ((select-column . 375)
+ (statement-continuation . 375))
+ ((select-column . 375)
+ (statement-continuation . 375))
+ ((select-column . 375)
+ (statement-continuation . 375))
+ ((select-column . 375)
+ (statement-continuation . 375))
+ ((select-column . 375)
+ (statement-continuation . 375))
+ ((select-clause . 375)
+ (statement-continuation . 375))
+ ((select-table . 565)
+ (statement-continuation . 375))
+ ((select-clause . 375)
+ (statement-continuation . 375))
+ (((in-select-clause "where")
+ . 624)
+ (statement-continuation . 375))
+ (((in-select-clause "where")
+ . 624)
+ (statement-continuation . 375))
+ (((in-select-clause "where")
+ . 624)
+ (statement-continuation . 375))
+ (((in-select-clause "where")
+ . 624)
+ (statement-continuation . 375))
+ (((in-select-clause "where")
+ . 624)
+ (statement-continuation . 375))
+ (((in-select-clause "where")
+ . 624)
+ (statement-continuation . 375))
+ ((select-clause . 888)
+ (nested-statement-continuation . 887)
+ (statement-continuation . 887))
+ ((select-table . 922)
+ (nested-statement-continuation . 887)
+ (statement-continuation . 887))
+ ((select-clause . 888)
+ (nested-statement-continuation . 887)
+ (statement-continuation . 887))
+ (((in-select-clause "where")
+ . 1029)
+ (nested-statement-continuation . 887)
+ (statement-continuation . 887))
+ (((in-select-clause "where")
+ . 1029)
+ (nested-statement-continuation . 887)
+ (statement-continuation . 887))
+ (((in-select-clause "where")
+ . 1029)
+ (nested-statement-continuation . 887)
+ (statement-continuation . 887))
+ (((in-select-clause "where")
+ . 1029)
+ (nested-statement-continuation . 887)
+ (statement-continuation . 887))
+ (((in-select-clause "where")
+ . 1029)
+ (nested-statement-continuation . 887)
+ (statement-continuation . 887))
+ (((in-select-clause "where")
+ . 624)
+ (statement-continuation . 375))
+ ((toplevel . 33))
+ ((toplevel . 33))
+ ((delete-clause . 1417)
+ (statement-continuation . 1417))
+ (((in-delete-clause "where")
+ . 1442)
+ (statement-continuation . 1417))
+ ((nested-statement-continuation . 1481)
+ (statement-continuation . 1481))
+ (((in-delete-clause "where")
+ . 1442)
+ (statement-continuation . 1417))
+ (((in-delete-clause "where")
+ . 1442)
+ (statement-continuation . 1417))
+ ((toplevel . 33))
+ ((toplevel . 33))
+ ((update-clause . 1576)
+ (statement-continuation . 1576))
+ (((in-update-clause "set")
+ . 1595)
+ (statement-continuation . 1576))
+ ((update-clause . 1576)
+ (statement-continuation . 1576))
+ (((in-update-clause "where")
+ . 1674)
+ (statement-continuation . 1576))
+ ((nested-statement-continuation . 1710)
+ (statement-continuation . 1710))
+ (((in-update-clause "where")
+ . 1674)
+ (statement-continuation . 1576))
+ (((in-update-clause "where")
+ . 1674)
+ (statement-continuation . 1576))
+ ((toplevel . 33))
+ ((toplevel . 33))
+ (((in-insert-clause "insert into")
+ . 1800)
+ (statement-continuation . 1800))
+ ((nested-statement-continuation . 1830)
+ (statement-continuation . 1830))
+ ((nested-statement-continuation . 1830)
+ (statement-continuation . 1830))
+ ((insert-clause . 1800)
+ (statement-continuation . 1800))
+ ((select-column . 1902)
+ (statement-continuation . 1800))
+ ((select-column . 1902)
+ (statement-continuation . 1800))
+ ((select-column . 1902)
+ (statement-continuation . 1800))
+ ((select-column . 1902)
+ (statement-continuation . 1800))
+ ((select-column . 1902)
+ (statement-continuation . 1800))
+ ((select-column . 1902)
+ (statement-continuation . 1800))
+ ((nested-statement-continuation . 2081)
+ (statement-continuation . 2081))
+ ((select-column . 1902)
+ (statement-continuation . 1800))
+ ((nested-statement-continuation . 2174)
+ (statement-continuation . 2174))
+ ((select-column . 1902)
+ (statement-continuation . 1800))
+ ((select-clause . 1902)
+ (statement-continuation . 1800))
+ ((select-column . 2293)
+ (nested-statement-continuation . 2292)
+ (statement-continuation . 2292))
+ ((select-column . 2293)
+ (nested-statement-continuation . 2292)
+ (statement-continuation . 2292))
+ ((nested-statement-continuation . 2403)
+ (statement-continuation . 2403))
+ ((nested-statement-continuation . 2403)
+ (statement-continuation . 2403))
+ ((select-clause . 2293)
+ (nested-statement-continuation . 2292)
+ (statement-continuation . 2292))
+ ((select-clause . 2278)
+ (nested-statement-continuation . 2277)
+ (statement-continuation . 2277))
+ ((select-table-continuation . 2271)
+ (statement-continuation . 1800))
+ ((select-column . 2650)
+ (nested-statement-continuation . 2649)
+ (statement-continuation . 2649))
+ ((select-clause . 2650)
+ (nested-statement-continuation . 2649)
+ (statement-continuation . 2649))
+ ((select-join-condition . 2638)
+ (statement-continuation . 1800))
+ ((select-clause . 1902)
+ (statement-continuation . 1800))
+ (((in-select-clause "group by")
+ . 2773)
+ (statement-continuation . 1800))
+ ((select-clause . 1902)
+ (statement-continuation . 1800))
+ (((in-select-clause "order by")
+ . 2802)
+ (statement-continuation . 1800))
+ (((in-select-clause "order by")
+ . 2802)
+ (statement-continuation . 1800))
+ ((comment-start . 33)
+ (toplevel . 33))
+ ((comment-start . 33)
+ (toplevel . 33))
+ ((comment-start . 33)
+ (toplevel . 33))
+ ((toplevel . 33)))
\ No newline at end of file
--- /dev/null
+clear columns
+set linesize 2500
+-- set trimout on
+-- set trimspool on
+
+select sysdate from dual;
+
+select col1, 'a long line of text ending with a single word'
+ || col2
+ || col3
+ || 'some text' as composed_column,
+ col4
+ || col5 as composed_column2
+ from my_table
+ where cond1 = fct1
+ || 'another text'
+ and cond2 = 2;
+
+select atc.column_name,
+ atc.data_type,
+ data_length,
+ data_precision,
+ nullable,
+ data_scale,
+ nvl(substr(comments, 1, 100), atc.column_name) comments
+ from all_tab_columns atc,
+ all_col_comments acc
+ where atc.owner = acc.owner
+ and atc.table_name = acc.table_name
+ and atc.column_name = acc.column_name
+ and atc.owner = user
+ and atc.table_name = 'MY_TABLE'
+ and atc.column_name = p_column_name
+ and not exists (select 1
+ from all_tab_columns atc1,
+ all_col_comments acc1
+ where atc1.owner = acc1.owner
+ and atc1.table_name = acc1.table_name
+ and atc1.column_name = acc1.column_name
+ and atc1.owner = atc.owner
+ and atc1.table_name = atc.table_name
+ and acc1.column_name = acc.column_name)
+ ;
+
+delete from my_table mt
+ where col_1 = v_col1
+ and (col_2 = v_col2
+ or col_3 = v_col3)
+ and col_42 = '42'
+ ;
+
+update my_table
+ set col1_has_a_long_name = value1,
+ col2_is_short = value2
+ where cond1 is not null
+ and ( col_2 = v_col2
+ or col_3 = v_col3)
+ and col_42 = '42'
+ ;
+
+insert into xyzxx
+ ( aaa, xxx, bbb, ccc,
+ ddd, eee, fff, ggg,
+ hhh )
+select aaa,
+ xxx,
+ max (m.b1) as bbb,
+ min (m.b1) as ccc,
+ coalesce (max (n.c2), 0) as ddd,
+ coalesce (min (n.c2), 0) as eee,
+ max (m.b1) over ( partition by c2
+ order by aaa desc ) as fff,
+ min (m.b1) over ( partition by c2
+ order by aaa desc ) as ggg,
+ avg (n.c2) as hhh
+ from (select * from (select aaa,
+ jjj + kkk as b1,
+ row_number () over ( partition by qqq
+ order by rrr,
+ sss ) as rn
+ from mno) a_nested_nested
+ where rn = 1) m
+ inner join (select aaa,
+ nnn + ooo as c2
+ from pqr) n
+ using (aaa),
+ group by aaa,
+ xxx
+ order by xxx desc,
+ aaa asc
+ ;
+-- Local Variables:
+-- sql-product: oracle
+-- End:
--- /dev/null
+(((toplevel . 1))
+ ((toplevel . 21))
+ ((declare-statement . 21))
+ ((declare-statement . 21))
+ (((block-start begin)
+ . 21)
+ (declare-statement . 21))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ ((case-clause . 126)
+ (statement-continuation . 117))
+ ((case-clause . 126)
+ (statement-continuation . 117))
+ ((case-clause . 126)
+ (statement-continuation . 117))
+ (((block-end case "")
+ . 126)
+ (statement-continuation . 117))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ (((block-start when)
+ . 290)
+ ((in-block case "")
+ . 290))
+ (((block-start when)
+ . 290)
+ ((in-block case "")
+ . 290))
+ (((block-start when)
+ . 290)
+ ((in-block case "")
+ . 290))
+ (((block-start else)
+ . 290)
+ ((in-block case "")
+ . 290))
+ (((block-end case "")
+ . 290)
+ ((in-block else "")
+ . 392))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ (((in-begin-block toplevel-block "")
+ . 78))
+ (((block-end toplevel-block "")
+ . 78)
+ ((in-begin-block toplevel-block "")
+ . 78))
+ ((toplevel . 21))
+ ((comment-start . 21)
+ (toplevel . 21))
+ ((comment-start . 21)
+ (toplevel . 21))
+ ((comment-start . 21)
+ (toplevel . 21))
+ ((toplevel . 21)))
+
+
\ No newline at end of file
--- /dev/null
+set serveroutput on
+declare
+ dummy varchar2(25);
+ ind number := 2;
+begin
+ dbms_output.enable(null);
+
+ dummy := case
+ when ind = 1 then 'Guy'
+ when ind = 2 then 'Abc'
+ else 'World'
+ end;
+
+ dbms_output.put_line('Hello ' || dummy);
+
+ case ind
+ when 1 then dummy := 'Guy';
+ when 2 then dummy := 'Abc';
+ when 3 then dummy := 'Def';
+ else dummy := 'World';
+ end case;
+
+ dbms_output.put_line('Hello ' || dummy);
+end;
+/
+-- Local Variables:
+-- sql-product: oracle
+-- End:
--- /dev/null
+(((toplevel . 1))
+ (((package-body "my_pacakge")
+ . 1))
+ (((package-body "my_pacakge")
+ . 1))
+ ((statement-continuation . 67))
+ ((select-clause . 84)
+ (statement-continuation . 67))
+ ((select-clause . 84)
+ (statement-continuation . 67))
+ (((in-select-clause "where")
+ . 122)
+ (statement-continuation . 67))
+ (((package-body "my_pacakge")
+ . 1))
+ (((package-body "my_pacakge")
+ . 1))
+ (((package-body "my_pacakge")
+ . 1))
+ (((package-body "my_pacakge")
+ . 1))
+ (((defun-start "get_my_value")
+ . 198))
+ (((defun-start "get_my_value")
+ . 198))
+ (((defun-start "get_my_value")
+ . 198))
+ ((statement-continuation . 286))
+ ((select-clause . 340)
+ (statement-continuation . 286))
+ ((select-clause . 340)
+ (statement-continuation . 286))
+ (((defun-start "get_my_value")
+ . 198))
+ (((defun-start "get_my_value")
+ . 198))
+ (((block-start begin)
+ . 198)
+ ((defun-start "get_my_value")
+ . 198))
+ (((in-begin-block defun "get_my_value")
+ . 437))
+ (((in-block loop "")
+ . 445))
+ (((block-end loop "")
+ . 445)
+ ((in-block loop "")
+ . 445))
+ (((block-end defun "get_my_value")
+ . 437)
+ ((in-begin-block defun "get_my_value")
+ . 437))
+ (((package-body "my_pacakge")
+ . 1))
+ (((block-start begin)
+ . 1)
+ ((package-body "my_pacakge")
+ . 1))
+ (((in-begin-block package "my_pacakge")
+ . 533))
+ (((in-block loop "")
+ . 541))
+ (((block-end loop "")
+ . 541)
+ ((in-block loop "")
+ . 541))
+ (((in-begin-block package "my_pacakge")
+ . 533))
+ (((in-begin-block package "my_pacakge")
+ . 533))
+ (((in-begin-block nil "")
+ . 614))
+ (((block-end nil "")
+ . 614)
+ ((in-begin-block nil "")
+ . 614))
+ (((in-begin-block package "my_pacakge")
+ . 533))
+ (((in-begin-block package "my_pacakge")
+ . 533))
+ (((block-end package "my_pacakge")
+ . 533)
+ ((in-begin-block package "my_pacakge")
+ . 533))
+ ((toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1)))
+
--- /dev/null
+create or replace package body my_pacakge authid current user is
+
+ cursor cur1 is
+ select 1 dummy
+ from my_table
+ where 1 = 1
+ and col1 = p_col1;
+
+ p_col1 my_table.col1%type := 42;
+
+ function get_my_value(p_param1 in my_table.col1%type)
+ return my_table.col2%type is
+
+ cursor cur2 (p_val_col1 in my_table.col1%type) is
+ select col2
+ from my_table
+ where col1 = p_val_col1;
+
+ v_result my_table.col2%type;
+ begin
+ for rec in cur2(p_param1) loop
+ v_result := rec.col2;
+ end loop;
+ end get_my_value;
+
+begin
+ for rec in cur1 loop
+ dbms_output.put_line(rec.dummy);
+ end loop;
+
+ begin
+ null;
+ end;
+
+ dbms_output.put_line(get_my_value(p_col1));
+end my_pacakge;
+/
+-- Local Variables:
+-- sql-product: oracle
+-- End:
--- /dev/null
+(((toplevel . 1))
+ ((toplevel . 21))
+ ((toplevel . 49))
+ ((toplevel . 49))
+ ((declare-statement . 50))
+ ((declare-statement . 50))
+ (((block-start begin)
+ . 50)
+ (declare-statement . 50))
+ (((in-begin-block toplevel-block "")
+ . 106))
+ (((in-begin-block toplevel-block "")
+ . 106))
+ (((in-block if "")
+ . 142))
+ ((statement-continuation . 171))
+ ((statement-continuation . 171))
+ (((in-block if "")
+ . 142))
+ ((statement-continuation . 253))
+ ((statement-continuation . 253))
+ (((block-start elsif)
+ . 142)
+ ((in-block if "")
+ . 142))
+ (((in-block elsif "")
+ . 299))
+ ((statement-continuation . 331))
+ ((statement-continuation . 331))
+ (((in-block elsif "")
+ . 299))
+ ((statement-continuation . 413))
+ ((statement-continuation . 413))
+ (((block-start else)
+ . 299)
+ ((in-block elsif "")
+ . 299))
+ (((in-block else "")
+ . 459))
+ (((in-block else "")
+ . 459))
+ (((block-end if "")
+ . 142)
+ ((in-block else "")
+ . 459))
+ (((in-begin-block toplevel-block "")
+ . 106))
+ (((in-begin-block toplevel-block "")
+ . 106))
+ ((statement-continuation . 531))
+ ((statement-continuation . 531))
+ (((in-begin-block toplevel-block "")
+ . 106))
+ ((statement-continuation . 607))
+ ((statement-continuation . 607))
+ (((in-begin-block toplevel-block "")
+ . 106))
+ (((in-begin-block toplevel-block "")
+ . 106))
+ (((block-end toplevel-block "")
+ . 106)
+ ((in-begin-block toplevel-block "")
+ . 106))
+ ((toplevel . 49))
+ ((comment-start . 49)
+ (toplevel . 49))
+ ((comment-start . 49)
+ (toplevel . 49))
+ ((comment-start . 49)
+ (toplevel . 49))
+ ((comment-start . 49)
+ (toplevel . 49))
+ ((comment-start . 49)
+ (toplevel . 49))
+ ((comment-start . 49)
+ (toplevel . 49))
+ ((toplevel . 49)))
\ No newline at end of file
--- /dev/null
+set serveroutput on
+set trimout on trimspool on
+
+declare
+ var1 varchar2(25000);
+ var2 varchar2(25000);
+begin
+ dbms_output.enable(null);
+ if var1 is not null then
+ var2 := 'var1 is set : -*-'
+ || var1
+ || '-*- some text after ...';
+ var1 := '-*-'
+ || var1
+ || '-*-';
+ elsif var2 is not null then
+ var1 := 'var2 is set : -*-'
+ || var2
+ || '-*- some text after ...';
+ var2 := '-*-'
+ || var2
+ || '-*-';
+ else
+ var2 := '-*-empty-*-';
+ var1 := '-*-empty-*-';
+ end if;
+
+ var2 := 'var1 is set : -*-'
+ || var1
+ || '-*- some text after ...';
+ var1 := 'var2 is set : -*-'
+ || var2
+ || '-*- some text after ...';
+ dbms_output.put_line(var1);
+ dbms_output.put_line(var2);
+end;
+/
+-- Local Variables:
+-- mode: sql
+-- tab-width: 2
+-- indent-tabs-mode: nil
+-- sql-product: oracle
+-- End:
--- /dev/null
+(((toplevel . 1))
+ ((declare-statement . 1))
+ ((declare-statement . 1))
+ (((block-start begin)
+ . 1)
+ (declare-statement . 1))
+ (((in-begin-block toplevel-block "")
+ . 62))
+ (((in-begin-block nil "")
+ . 70))
+ (((in-block if "")
+ . 80))
+ (((in-block if "")
+ . 80))
+ (((block-start elsif)
+ . 80)
+ ((in-block if "")
+ . 80))
+ (((in-block elsif "")
+ . 124))
+ (((in-block elsif "")
+ . 124))
+ (((block-start else)
+ . 124)
+ ((in-block elsif "")
+ . 124))
+ (((in-block else "")
+ . 166))
+ (((block-end if "")
+ . 80)
+ ((in-block else "")
+ . 166))
+ (((in-begin-block nil "")
+ . 70))
+ (((block-start when)
+ . 215)
+ ((in-block case "")
+ . 215))
+ (((block-start when)
+ . 215)
+ ((in-block case "")
+ . 215))
+ (((block-start when)
+ . 215)
+ ((in-block case "")
+ . 215))
+ (((block-start else)
+ . 215)
+ ((in-block case "")
+ . 215))
+ (((block-end case "")
+ . 215)
+ ((in-block else "")
+ . 325))
+ (((block-start exception)
+ . 70)
+ ((in-begin-block nil "")
+ . 70))
+ (((in-block exception)
+ . 364))
+ (((in-block exception-handler)
+ . 378)
+ ((in-block exception "")
+ . 364))
+ (((in-block exception-handler)
+ . 378)
+ ((in-block exception "")
+ . 364))
+ (((in-block exception "")
+ . 364))
+ (((in-block exception-handler "")
+ . 432)
+ (statement-continuation . 432))
+ (((in-block exception-handler)
+ . 432)
+ ((in-block exception "")
+ . 364))
+ (((in-block exception "")
+ . 364))
+ (((in-block exception-handler "")
+ . 486)
+ (statement-continuation . 486))
+ (((block-end nil "")
+ . 70)
+ ((in-block exception "")
+ . 364))
+ (((block-end toplevel-block "")
+ . 62)
+ ((in-begin-block toplevel-block "")
+ . 62))
+ ((toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1)))
--- /dev/null
+declare
+ dummy number;
+ e_stop_program exception;
+begin
+ begin
+ if 1 = 1 then
+ proc1;
+ proc2;
+ elsif 1 = 2
+ proc3;
+ proc4;
+ else
+ raise e_stop_program;
+ end if;
+ case ind
+ when 1 then dummy := 'Guy';
+ when 2 then dummy := 'Abc';
+ when 3 then dummy := 'Def';
+ else dummy := 'World';
+ end case;
+ exception
+ when no_data_found then
+ proc1;
+ proc2;
+ when too_many_rows then
+ proc3;
+ proc4;
+ when others then
+ proc5;
+ end;
+end;
+/
+-- Local Variables:
+-- mode: sqlind-minor
+-- mode: sql
+-- sql-product: oracle
+-- End:
--- /dev/null
+(((toplevel . 1))
+ ((select-column . 1)
+ (statement-continuation . 1))
+ ((case-clause . 19)
+ (select-column-continuation . 1)
+ (statement-continuation . 1))
+ ((case-clause . 19)
+ (select-column-continuation . 1)
+ (statement-continuation . 1))
+ ((case-clause . 19)
+ (select-column-continuation . 1)
+ (statement-continuation . 1))
+ (((block-end case "")
+ . 19)
+ (select-column-continuation . 1)
+ (statement-continuation . 1))
+ ((select-clause . 1)
+ (statement-continuation . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((declare-statement . 141))
+ (((block-start begin)
+ . 141)
+ (declare-statement . 141))
+ (((in-begin-block toplevel-block "")
+ . 165))
+ (((block-start when)
+ . 173)
+ ((in-block case "")
+ . 173))
+ (((in-block when)
+ . 184)
+ ((in-block case "")
+ . 173))
+ (((in-block when)
+ . 184)
+ ((in-block case "")
+ . 173))
+ (((block-start when)
+ . 173)
+ ((in-block case "")
+ . 173))
+ (((in-block when)
+ . 239)
+ ((in-block case "")
+ . 173))
+ (((in-block when)
+ . 239)
+ ((in-block case "")
+ . 173))
+ (((block-start when)
+ . 173)
+ ((in-block case "")
+ . 173))
+ (((in-block when)
+ . 294)
+ ((in-block case "")
+ . 173))
+ (((block-start else)
+ . 173)
+ ((in-block case "")
+ . 173))
+ (((in-block else "")
+ . 329))
+ (((block-end case "")
+ . 173)
+ ((in-block else "")
+ . 329))
+ (((block-end toplevel-block "")
+ . 165)
+ ((in-begin-block toplevel-block "")
+ . 165))
+ ((toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1)))
+
\ No newline at end of file
--- /dev/null
+select y ,
+ case
+ when foo>5 then "great"
+ when foo=5 then "normal"
+ else "poor"
+ end as level
+ from bar;
+
+declare
+ dummy number;
+begin
+ case ind
+ when 1 then
+ dummy := 'Guy';
+ dummy1 := 'Abc';
+ when 2 then
+ dummy := 'Abc';
+ dummy1 := 'Abc';
+ when 3 then
+ dummy := 'Def';
+ else
+ dummy := 'World';
+ end case;
+end;
+/
+-- Local Variables:
+-- sql-product: oracle
+-- End:
--- /dev/null
+(0 2 2 2 0 2 0 0 0 0 0 0 0 0 0)
--- /dev/null
+(((toplevel . 1))
+ ((declare-statement . 1))
+ ((declare-statement . 1))
+ ((declare-statement . 1))
+ (((block-start begin)
+ . 1)
+ (declare-statement . 1))
+ (((in-begin-block toplevel-block "")
+ . 135))
+ (((block-end toplevel-block "")
+ . 135)
+ ((in-begin-block toplevel-block "")
+ . 135))
+ ((toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1)))
--- /dev/null
+declare
+ function dummy return my_user.my_table%rowtype;
+ function dummy2 return my_user.my_table.my_col%type;
+ var dummy3 number;
+begin
+ null;
+end;
+
+-- Local Variables:
+-- mode: sql
+-- tab-width: 2
+-- indent-tabs-mode: nil
+-- sql-product: oracle
+-- End:
--- /dev/null
+(0 2 4 6 8 6 4 2 0 0 0 0 0 0 0 0 0 0)
--- /dev/null
+((((block-start begin)
+ . 1)
+ (toplevel . 1))
+ (((in-begin-block toplevel-block "")
+ . 1))
+ (((in-block if "")
+ . 9))
+ (((in-block loop "")
+ . 27))
+ (((in-block if "")
+ . 53))
+ (((block-end if "")
+ . 53)
+ ((in-block if "")
+ . 53))
+ (((block-end loop "")
+ . 27)
+ ((in-block loop "")
+ . 27))
+ (((block-end if "")
+ . 9)
+ ((in-block if "")
+ . 9))
+ (((block-end toplevel-block "")
+ . 1)
+ ((in-begin-block toplevel-block "")
+ . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1)))
+
--- /dev/null
+begin
+ if a = b then
+ for rec in cur loop
+ if c = d then
+ inst1;
+ end if;
+ end loop;
+ end if;
+end;
+/
+
+-- Local Variables:
+-- mode: sql
+-- tab-width: 2
+-- indent-tabs-mode: nil
+-- sql-product: oracle
+-- End:
--- /dev/null
+(0 2 0 0 0 0 0 0 0 0 0 0 0)
\ No newline at end of file
--- /dev/null
+(((toplevel . 1))
+ (((package "my_wonderfull_package")
+ . 1))
+ (((block-end package "my_wonderfull_package")
+ . 1)
+ ((package "my_wonderfull_package")
+ . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1)))
--- /dev/null
+create or replace package my_user.my_wonderfull_package as
+ function my_amazing_func return number;
+end my_wonderfull_package;
+/
+
+-- Local Variables:
+-- mode: sql
+-- mode: sqlind-minor
+-- tab-width: 2
+-- indent-tabs-mode: nil
+-- sql-product: oracle
+-- End:
--- /dev/null
+(((toplevel . 1))
+ ((select-column . 1)
+ (statement-continuation . 1))
+ ((case-clause . 23)
+ (select-column-continuation . 1)
+ (statement-continuation . 1))
+ ((case-clause . 23)
+ (select-column-continuation . 1)
+ (statement-continuation . 1))
+ ((case-clause . 23)
+ (select-column-continuation . 1)
+ (statement-continuation . 1))
+ (((block-end case "")
+ . 23)
+ (select-column-continuation . 1)
+ (statement-continuation . 1))
+ ((select-clause . 1)
+ (statement-continuation . 1))
+ ((select-clause . 1)
+ (statement-continuation . 1))
+ ((toplevel . 1)))
+
\ No newline at end of file
--- /dev/null
+select pk_id
+ , case
+ when t1.c1 is null then '(null)'
+ when t1.c1 in ('a', 'b','c','d','e') then 'a'
+ else t1.c1
+ end c1_alt
+ from t1
+ limit 10;
--- /dev/null
+(((toplevel . 1))
+ ((toplevel . 14))
+ ((toplevel . 17))
+ ((toplevel . 35))
+ ((toplevel . 38))
+ ((toplevel . 63))
+ ((toplevel . 66))
+ (((defun-start "[test]")
+ . 66))
+ (((defun-start "[test]")
+ . 66))
+ (((defun-start "[test]")
+ . 66))
+ (((defun-start "[test]")
+ . 66))
+ ((nested-statement-open . 130)
+ (statement-continuation . 130))
+ ((nested-statement-continuation . 130)
+ (statement-continuation . 130))
+ ((nested-statement-close . 130)
+ (statement-continuation . 130))
+ (((block-start is-or-as)
+ . 66)
+ ((defun-start "[test]")
+ . 66))
+ (((block-start begin)
+ . 66)
+ ((defun-start "[test]")
+ . 66))
+ (((in-begin-block defun "[test]")
+ . 209))
+ (((in-begin-block defun "[test]")
+ . 209))
+ (((in-begin-block defun "[test]")
+ . 209))
+ (((in-begin-block defun "[test]")
+ . 209))
+ (((block-end defun "[test]")
+ . 209)
+ ((in-begin-block defun "[test]")
+ . 209))
+ ((comment-start . 66)
+ (toplevel . 66))
+ ((comment-start . 66)
+ (toplevel . 66))
+ ((comment-start . 66)
+ (toplevel . 66))
+ ((toplevel . 66)))
+
\ No newline at end of file
--- /dev/null
+USE [testdb]
+GO
+SET ANSI_NULLS ON
+GO
+SET QUOTED_IDENTIFIER ON
+GO
+ALTER FUNCTION [dbo].[Test]
+()
+RETURNS
+@TestBatch TABLE
+(
+ [param] [varchar](20) NOT NULL,
+ [param2] [datetime] NOT NULL,
+)
+AS
+BEGIN
+ DECLARE @var int;
+ DECLARE @var2 int;
+ set @var = 100;
+ RETURN;
+END
+-- Local Variables:
+-- sql-product: ms
+-- End:
--- /dev/null
+(((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1))
+ (((create-statement view "v_sport_zone_for_session")
+ . 109))
+ ((select-column . 151)
+ ((create-statement view "v_sport_zone_for_session")
+ . 109))
+ ((select-column . 151)
+ ((create-statement view "v_sport_zone_for_session")
+ . 109))
+ ((select-clause . 151)
+ ((create-statement view "v_sport_zone_for_session")
+ . 109))
+ ((select-clause . 151)
+ ((create-statement view "v_sport_zone_for_session")
+ . 109))
+ (((in-select-clause "where")
+ . 299)
+ ((create-statement view "v_sport_zone_for_session")
+ . 109))
+ ((nested-statement-continuation . 341)
+ (statement-continuation . 341))
+ ((nested-statement-continuation . 340)
+ (statement-continuation . 340))
+ (((in-select-clause "where")
+ . 299)
+ ((create-statement view "v_sport_zone_for_session")
+ . 109))
+ ((toplevel . 1)))
+
\ No newline at end of file
--- /dev/null
+-- sql-indent used to think that the statement begins at the "FOR" keyword in
+-- "V_SPORT_ZONE_FOR_SESSION"
+create view V_SPORT_ZONE_FOR_SESSION as
+ select S.id as session_id,
+ VSZ.zone_id as zone_id,
+ VSZ.zone_metric_id as zone_metric_id
+ from A_SESSION S, V_SPORT_ZONE VSZ
+ where S.sport_id = VSZ.sport_id
+ and (((S.sub_sport_id is null or S.sub_sport_id = 0)
+ and (VSZ.sub_sport_id is null or VSZ.sub_sport_id = 0))
+ or S.sub_sport_id = VSZ.sub_sport_id)
+ and S.start_time between VSZ.valid_from and VSZ.valid_until;
--- /dev/null
+(((toplevel . 1))
+ ((comment-start . 20)
+ (nested-statement-open . 20)
+ (statement-continuation . 20))
+ ((nested-statement-continuation . 20)
+ (statement-continuation . 20))
+ ((select-column . 52)
+ (nested-statement-continuation . 20)
+ (statement-continuation . 20))
+ ((select-column . 52)
+ (nested-statement-continuation . 20)
+ (statement-continuation . 20))
+ ((select-clause . 52)
+ (nested-statement-continuation . 20)
+ (statement-continuation . 20))
+ ((with-clause . 1)
+ (statement-continuation . 1))
+ ((select-clause . 115)
+ (statement-continuation . 1))
+ ((toplevel . 1)))
--- /dev/null
+with some_table as (
+ -- a comment at the start
+ select col1,
+ col2,
+ col3
+ from some_table)
+select *
+ from some_table;
--- /dev/null
+((((block-start begin)
+ . 1)
+ (toplevel . 1))
+ (((in-begin-block toplevel-block "")
+ . 1))
+ (((in-begin-block toplevel-block "")
+ . 1))
+ (((in-begin-block toplevel-block "")
+ . 1))
+ ((labeled-statement-start . 87)
+ (statement-continuation . 87))
+ (((in-block loop "my_loop")
+ . 101))
+ (((block-end loop "my_loop")
+ . 101)
+ ((in-block loop "my_loop")
+ . 101))
+ (((in-begin-block toplevel-block "")
+ . 1))
+ (((in-begin-block toplevel-block "")
+ . 1))
+ (((in-begin-block toplevel-block "")
+ . 1))
+ ((labeled-statement-start . 229)
+ (statement-continuation . 229))
+ (((in-block loop "my_second_loop")
+ . 250))
+ (((in-block loop "my_second_loop")
+ . 250))
+ (((block-end loop "my_second_loop")
+ . 250)
+ ((in-block loop "my_second_loop")
+ . 250))
+ (((in-begin-block toplevel-block "")
+ . 1))
+ (((block-end toplevel-block "")
+ . 1)
+ ((in-begin-block toplevel-block "")
+ . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1)))
--- /dev/null
+begin
+ goto my_loop;
+ dbms_output.put_line('If you read it, you''re in trouble');
+ <<my_loop>>
+ for ind in 1..5 loop
+ dbms_output.put_line('ind: ' || ind);
+ end loop my_loop;
+
+ dbms_output.put_line('end of my_loop');
+ <<my_second_loop>>
+ while true loop
+ dbms_output.put_line('in my_second_loop');
+ goto out_of_loop;
+ end loop my_second_loop;
+ dbms_output.put_line('end of my_second_loop');
+end;
+/
+
+-- Local Variables:
+-- indent-tabs-mode: nil
+-- mode: sql
+-- mode: sqlind-minor
+-- sql-product: oracle
+-- tab-width: 2
+-- End:
--- /dev/null
+((((block-start begin)
+ . 1)
+ (toplevel . 1))
+ (((in-begin-block toplevel-block "")
+ . 1))
+ (((in-block if "")
+ . 9))
+ (((in-block if "")
+ . 9))
+ (((in-block loop "my_third_loop")
+ . 48))
+ (((in-block loop "my_third_loop")
+ . 48))
+ (((block-end loop "my_third_loop")
+ . 48)
+ ((in-block loop "my_third_loop")
+ . 48))
+ (((in-block if "")
+ . 9))
+ (((block-end if "")
+ . 9)
+ ((in-block if "")
+ . 9))
+ (((block-end toplevel-block "")
+ . 1)
+ ((in-begin-block toplevel-block "")
+ . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1)))
--- /dev/null
+begin
+ if true then
+ <<my_third_loop>>
+ while true loop
+ dbms_output.put_line('in my_second_loop');
+ exit my_third_loop;
+ end loop my_third_loop;
+ dbms_output.put_line('end of my_second_loop');
+ end if;
+end;
+/
+
+-- Local Variables:
+-- indent-tabs-mode: nil
+-- mode: sql
+-- mode: sqlind-minor
+-- sql-product: oracle
+-- tab-width: 2
+-- End:
--- /dev/null
+(0 7 7 7 7 7 7 7 0 7 0 0 0 0 0 0 0 19 26 19 19 19 19 19 19 0 0 0 0 0 0 0 0 0 0)
--- /dev/null
+select distinct
+ atc.column_name,
+ atc.data_type,
+ data_length,
+ data_precision,
+ nullable,
+ data_scale,
+ nvl(substr(comments, 1, 100), atc.column_name) comments
+from all_tab_columns atc,
+ all_col_comments acc
+where atc.owner = acc.owner
+and atc.table_name = acc.table_name
+and atc.column_name = acc.column_name
+and atc.owner = user
+and atc.table_name = 'MY_TABLE'
+and atc.column_name = p_column_name
+and not exists (select 1
+ from all_tab_columns atc1,
+ all_col_comments acc1
+ where atc1.owner = acc1.owner
+ and atc1.table_name = acc1.table_name
+ and atc1.column_name = acc1.column_name
+ and atc1.owner = atc.owner
+ and atc1.table_name = atc.table_name
+ and acc1.column_name = acc.column_name)
+;
+
+-- Local Variables:
+-- mode: sql
+-- mode: sqlind-minor
+-- tab-width: 2
+-- indent-tabs-mode: nil
+-- sql-product: oracle
+-- End:
--- /dev/null
+(0 4 7 0 0 0 0 0 0 0 0 0 0)
--- /dev/null
+select mt.myVar
+ || '-*-' myVarLabel,
+ myVar2
+from myTable mt;
+
+-- Local Variables:
+-- mode: sql
+-- mode: sqlind-minor
+-- tab-width: 2
+-- indent-tabs-mode: nil
+-- sql-product: oracle
+-- End:
--- /dev/null
+(0 4 7 0 0 0 0 0 0 0 0 0 0 0 0)
--- /dev/null
+select my_col || chr(10)
+ || '-*-' data_type,
+ x
+from dual;
+
+
+-- Local Variables:
+-- mode: sql
+-- mode: sqlind-minor
+-- tab-width: 2
+-- indent-tabs-mode: nil
+-- sql-product: oracle
+-- End:
+
--- /dev/null
+(((toplevel . 1))
+ (((block-start begin)
+ . 1)
+ ((defun-start "foo_1")
+ . 1))
+ (((in-begin-block defun "foo_1")
+ . 1))
+ (((block-end defun "foo_1")
+ . 1)
+ ((in-begin-block defun "foo_1")
+ . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((in-begin-block defun "foo_2")
+ . 60))
+ (((block-end defun "foo_2")
+ . 60)
+ ((in-begin-block defun "foo_2")
+ . 60))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((block-start is-or-as)
+ . 119)
+ ((defun-start "foo_3")
+ . 119))
+ (((in-begin-block defun "foo_3")
+ . 119))
+ (((block-end defun "foo_3")
+ . 119)
+ ((in-begin-block defun "foo_3")
+ . 119))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((block-start is-or-as)
+ . 178)
+ ((defun-start "foo_4")
+ . 178))
+ (((block-start begin)
+ . 178)
+ ((defun-start "foo_4")
+ . 178))
+ (((in-begin-block defun "foo_4")
+ . 178))
+ (((block-end defun "foo_4")
+ . 178)
+ ((in-begin-block defun "foo_4")
+ . 178))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1)))
--- /dev/null
+create function foo_1() returns int AS
+$$
+ select 5;
+$$;
+
+create function foo_2() returns int AS $$
+ select 5;
+$$;
+
+create function foo_3() returns int
+AS $$
+ select 5;
+$$;
+
+create function foo_4() returns int
+AS
+$$
+ select 5;
+$$;
+
+SELECT a();
+
+-- Local Variables:
+-- mode: sql
+-- mode: sqlind-minor
+-- tab-width: 2
+-- indent-tabs-mode: nil
+-- sql-product: postgres
+-- End:
--- /dev/null
+(((toplevel . 1))
+ (((create-statement view "myview")
+ . 1))
+ (((create-statement view "myview")
+ . 1))
+ (((create-statement view "myview")
+ . 1))
+ (((create-statement view "myview")
+ . 1))
+ (((create-statement view "myview")
+ . 1))
+ ((select-column . 112)
+ ((create-statement view "myview")
+ . 1))
+ ((select-clause . 112)
+ ((create-statement view "myview")
+ . 1))
+ ((select-table-continuation . 208)
+ ((create-statement view "myview")
+ . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((create-statement table "foo")
+ . 282))
+ ((select-column . 328)
+ ((create-statement table "foo")
+ . 282))
+ ((select-clause . 328)
+ ((create-statement table "foo")
+ . 282))
+ ((select-table-continuation . 424)
+ ((create-statement table "foo")
+ . 282))
+ ((toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1)))
+
\ No newline at end of file
--- /dev/null
+create or replace
+ algorithm = undefined
+ definer = user@localhost
+ sql security definer
+ view myview as
+ select distinct table1.fielda as firstfield,
+ table2.fieldb as secondfield
+ from table1
+ join table2 on table1.table1id = table2.fktable1;
+
+create temporary table if not exists foo as
+ select distinct table1.fielda as firstfield,
+ table2.fieldb as secondfield
+ from table1
+ join table2 on table1.table1id = table2.fktable1;
+
+-- local variables:
+-- mode: sql
+-- sql-product: mysql
+-- end:
--- /dev/null
+(((toplevel . 1))
+ ((select-clause . 1)
+ (statement-continuation . 1))
+ ((select-table-continuation . 12)
+ (statement-continuation . 1))
+ ((select-join-condition . 29)
+ (statement-continuation . 1))
+ ((select-table-continuation . 12)
+ (statement-continuation . 1))
+ ((select-join-condition . 70)
+ (statement-continuation . 1))
+ ((select-table-continuation . 12)
+ (statement-continuation . 1))
+ ((select-join-condition . 111)
+ (statement-continuation . 1))
+ ((select-table-continuation . 12)
+ (statement-continuation . 1))
+ ((select-join-condition . 151)
+ (statement-continuation . 1))
+ ((select-table-continuation . 12)
+ (statement-continuation . 1))
+ ((select-join-condition . 192)
+ (statement-continuation . 1))
+ ((select-table-continuation . 12)
+ (statement-continuation . 1))
+ ((select-join-condition . 233)
+ (statement-continuation . 1))
+ ((select-table-continuation . 12)
+ (statement-continuation . 1))
+ ((select-join-condition . 276)
+ (statement-continuation . 1))
+ ((select-table-continuation . 12)
+ (statement-continuation . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((select-clause . 314)
+ (statement-continuation . 314))
+ ((select-table-continuation . 325)
+ (statement-continuation . 314))
+ ((select-join-condition . 342)
+ (statement-continuation . 314))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((select-clause . 376)
+ (statement-continuation . 376))
+ ((select-table-continuation . 387)
+ (statement-continuation . 376))
+ ((select-join-condition . 404)
+ (statement-continuation . 376))
+ ((select-table-continuation . 387)
+ (statement-continuation . 376))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((select-clause . 447)
+ (statement-continuation . 447))
+ ((select-table-continuation . 458)
+ (statement-continuation . 447))
+ ((select-join-condition . 475)
+ (statement-continuation . 447))
+ ((select-table-continuation . 458)
+ (statement-continuation . 447))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((select-clause . 519)
+ (statement-continuation . 519))
+ ((select-table-continuation . 530)
+ (statement-continuation . 519))
+ ((select-join-condition . 547)
+ (statement-continuation . 519))
+ ((select-table-continuation . 530)
+ (statement-continuation . 519))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((select-clause . 591)
+ (statement-continuation . 591))
+ ((select-table-continuation . 602)
+ (statement-continuation . 591))
+ ((select-join-condition . 619)
+ (statement-continuation . 591))
+ ((select-table-continuation . 602)
+ (statement-continuation . 591))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((select-clause . 666)
+ (statement-continuation . 666))
+ ((select-table-continuation . 677)
+ (statement-continuation . 666))
+ ((select-join-condition . 694)
+ (statement-continuation . 666))
+ ((select-table-continuation . 677)
+ (statement-continuation . 666))
+ ((toplevel . 1)))
\ No newline at end of file
--- /dev/null
+select *
+ from T1
+ inner join T2
+ on a
+ cross join T3
+ on b
+ left join T4
+ on c
+ right join T5
+ on d
+ cross join T6
+ on e
+ natural join T7
+ on f
+ join T8
+ on g
+ ;
+
+select *
+ from T1
+ inner join T2
+ on a;
+
+select *
+ from T1
+ left join T2
+ on a
+ ;
+
+select *
+ from T1
+ right join T2
+ on a
+ ;
+
+select *
+ from T1
+ cross join T2
+ on a
+ ;
+
+select *
+ from T1
+ natural join T2
+ on a
+ ;
+
+
+select *
+ from T1
+ join T2
+ on a
+ ;
--- /dev/null
+((((block-start begin)
+ . 1)
+ (toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((block-start begin)
+ . 1)
+ (toplevel . 1))
+ ((statement-continuation . 89))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((block-start begin)
+ . 1)
+ (toplevel . 1))
+ ((statement-continuation . 179))
+ ((statement-continuation . 179))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1)))
+
\ No newline at end of file
--- /dev/null
+begin transaction;
+create table my_table ( id text );
+select id from my_table;
+commit;
+
+begin
+ transaction;
+create table my_table ( id text );
+select id from my_table;
+commit;
+
+begin -- a comment
+
+ transaction;
+create table my_table ( id text );
+select id from my_table;
+commit;
--- /dev/null
+(((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((block-start begin)
+ . 1)
+ (toplevel . 1))
+ ((toplevel . 1))
+ (((in-begin-block toplevel nil)
+ . 79))
+ (((in-begin-block toplevel nil)
+ . 79))
+ (((in-begin-block toplevel nil)
+ . 79))
+ (((in-begin-block nil "")
+ . 129))
+ (((in-begin-block nil "")
+ . 129))
+ (((in-block if "")
+ . 155))
+ (((in-block if "")
+ . 155))
+ (((block-start elsif)
+ . 155)
+ ((in-block if "")
+ . 155))
+ ((select-clause . 231)
+ (nested-statement-continuation . 230)
+ (statement-continuation . 230))
+ (((in-block elsif "")
+ . 224))
+ (((in-block elsif "")
+ . 224))
+ (((block-start elsif)
+ . 224)
+ ((in-block elsif "")
+ . 224))
+ ((select-clause . 347)
+ (nested-statement-continuation . 346)
+ (statement-continuation . 346))
+ ((select-clause . 347)
+ (nested-statement-continuation . 346)
+ (statement-continuation . 346))
+ (((in-block elsif "")
+ . 340))
+ (((in-block elsif "")
+ . 340))
+ (((block-start elsif)
+ . 340)
+ ((in-block elsif "")
+ . 340))
+ ((select-clause . 496)
+ (nested-statement-continuation . 495)
+ (statement-continuation . 495))
+ ((select-table-continuation . 526)
+ (nested-statement-continuation . 495)
+ (statement-continuation . 495))
+ ((select-table-continuation . 526)
+ (nested-statement-continuation . 495)
+ (statement-continuation . 495))
+ ((select-clause . 496)
+ (nested-statement-continuation . 495)
+ (statement-continuation . 495))
+ (((in-block elsif "")
+ . 489))
+ (((in-block elsif "")
+ . 489))
+ (((block-start else)
+ . 489)
+ ((in-block elsif "")
+ . 489))
+ (((in-block else "")
+ . 698))
+ ((statement-continuation . 709))
+ ((statement-continuation . 709))
+ ((statement-continuation . 709))
+ ((statement-continuation . 709))
+ ((statement-continuation . 709))
+ ((statement-continuation . 709))
+ (((in-block loop "")
+ . 709))
+ ((statement-continuation . 877))
+ ((statement-continuation . 877))
+ ((statement-continuation . 877))
+ ((statement-continuation . 877))
+ (((block-end loop "")
+ . 709)
+ ((in-block loop "")
+ . 709))
+ (((block-end if "")
+ . 155)
+ ((in-block else "")
+ . 698))
+ (((block-end nil "")
+ . 129)
+ ((in-begin-block nil "")
+ . 129))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((in-begin-block toplevel nil)
+ . 1113))
+ (((in-begin-block toplevel nil)
+ . 1113))
+ (((in-begin-block toplevel nil)
+ . 1113))
+ (((in-begin-block toplevel nil)
+ . 1113))
+ (((in-begin-block nil "")
+ . 1187))
+ (((in-begin-block nil "")
+ . 1187))
+ (((in-begin-block nil "")
+ . 1187))
+ (((in-block if "")
+ . 1237))
+ (((in-block if "")
+ . 1237))
+ (((block-start elsif)
+ . 1237)
+ ((in-block if "")
+ . 1237))
+ (((in-block elsif "")
+ . 1306))
+ (((in-block elsif "")
+ . 1306))
+ (((block-start elsif)
+ . 1306)
+ ((in-block elsif "")
+ . 1306))
+ ((select-clause . 1388)
+ (nested-statement-continuation . 1387)
+ (statement-continuation . 1387))
+ ((select-clause . 1388)
+ (nested-statement-continuation . 1387)
+ (statement-continuation . 1387))
+ (((in-select-clause "where")
+ . 1443)
+ (nested-statement-continuation . 1387)
+ (statement-continuation . 1387))
+ (((in-block elsif "")
+ . 1381))
+ (((in-block elsif "")
+ . 1381))
+ (((block-start elsif)
+ . 1381)
+ ((in-block elsif "")
+ . 1381))
+ ((select-clause . 1547)
+ (nested-statement-continuation . 1546)
+ (statement-continuation . 1546))
+ ((select-table-continuation . 1577)
+ (nested-statement-continuation . 1546)
+ (statement-continuation . 1546))
+ ((select-table-continuation . 1577)
+ (nested-statement-continuation . 1546)
+ (statement-continuation . 1546))
+ ((select-clause . 1547)
+ (nested-statement-continuation . 1546)
+ (statement-continuation . 1546))
+ (((in-select-clause "where")
+ . 1681)
+ (nested-statement-continuation . 1546)
+ (statement-continuation . 1546))
+ (((in-block elsif "")
+ . 1540))
+ (((in-block elsif "")
+ . 1540))
+ (((block-start elsif)
+ . 1540)
+ ((in-block elsif "")
+ . 1540))
+ ((select-clause . 1785)
+ (nested-statement-continuation . 1784)
+ (statement-continuation . 1784))
+ ((select-table-continuation . 1815)
+ (nested-statement-continuation . 1784)
+ (statement-continuation . 1784))
+ ((select-table-continuation . 1815)
+ (nested-statement-continuation . 1784)
+ (statement-continuation . 1784))
+ ((select-table-continuation . 1815)
+ (nested-statement-continuation . 1784)
+ (statement-continuation . 1784))
+ ((select-clause . 1785)
+ (nested-statement-continuation . 1784)
+ (statement-continuation . 1784))
+ (((in-select-clause "where")
+ . 1959)
+ (nested-statement-continuation . 1784)
+ (statement-continuation . 1784))
+ (((in-block elsif "")
+ . 1778))
+ (((in-block elsif "")
+ . 1778))
+ (((block-start else)
+ . 1778)
+ ((in-block elsif "")
+ . 1778))
+ (((in-block else "")
+ . 2057))
+ ((statement-continuation . 2068))
+ ((statement-continuation . 2068))
+ ((statement-continuation . 2068))
+ ((statement-continuation . 2068))
+ ((statement-continuation . 2068))
+ ((statement-continuation . 2068))
+ (((in-block loop "")
+ . 2068))
+ ((statement-continuation . 2249))
+ ((statement-continuation . 2249))
+ ((statement-continuation . 2249))
+ ((statement-continuation . 2249))
+ (((block-end loop "")
+ . 2068)
+ ((in-block loop "")
+ . 2068))
+ (((block-end if "")
+ . 1237)
+ ((in-block else "")
+ . 2057))
+ (((block-end nil "")
+ . 1187)
+ ((in-begin-block nil "")
+ . 1187))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((in-begin-block toplevel nil)
+ . 2479))
+ (((in-begin-block toplevel nil)
+ . 2479))
+ (((in-begin-block toplevel nil)
+ . 2479))
+ (((in-begin-block nil "")
+ . 2529))
+ (((in-begin-block nil "")
+ . 2529))
+ (((in-block if "")
+ . 2556))
+ (((in-block if "")
+ . 2556))
+ (((block-start elsif)
+ . 2556)
+ ((in-block if "")
+ . 2556))
+ ((select-clause . 2633)
+ (nested-statement-continuation . 2632)
+ (statement-continuation . 2632))
+ (((in-block elsif "")
+ . 2626))
+ (((in-block elsif "")
+ . 2626))
+ (((block-start elsif)
+ . 2626)
+ ((in-block elsif "")
+ . 2626))
+ ((select-clause . 2750)
+ (nested-statement-continuation . 2749)
+ (statement-continuation . 2749))
+ ((select-table-continuation . 2780)
+ (nested-statement-continuation . 2749)
+ (statement-continuation . 2749))
+ ((select-clause . 2750)
+ (nested-statement-continuation . 2749)
+ (statement-continuation . 2749))
+ (((in-block elsif "")
+ . 2743))
+ (((in-block elsif "")
+ . 2743))
+ (((block-start else)
+ . 2743)
+ ((in-block elsif "")
+ . 2743))
+ (((in-block else "")
+ . 2913))
+ ((statement-continuation . 2924))
+ ((statement-continuation . 2924))
+ ((statement-continuation . 2924))
+ ((statement-continuation . 2924))
+ ((statement-continuation . 2924))
+ (((in-block loop "")
+ . 2924))
+ ((statement-continuation . 3082))
+ ((statement-continuation . 3082))
+ ((statement-continuation . 3082))
+ ((statement-continuation . 3082))
+ (((block-end loop "")
+ . 2924)
+ ((in-block loop "")
+ . 2924))
+ (((block-end if "")
+ . 2556)
+ ((in-block else "")
+ . 2913))
+ (((block-end nil "")
+ . 2529)
+ ((in-begin-block nil "")
+ . 2529))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((in-begin-block toplevel nil)
+ . 3311))
+ (((in-begin-block toplevel nil)
+ . 3311))
+ (((in-begin-block toplevel nil)
+ . 3311))
+ (((in-begin-block nil "")
+ . 3361))
+ (((in-begin-block nil "")
+ . 3361))
+ (((in-block if "")
+ . 3395))
+ (((in-block if "")
+ . 3395))
+ (((block-start elsif)
+ . 3395)
+ ((in-block if "")
+ . 3395))
+ ((select-clause . 3472)
+ (nested-statement-continuation . 3471)
+ (statement-continuation . 3471))
+ (((in-block elsif "")
+ . 3465))
+ (((in-block elsif "")
+ . 3465))
+ (((block-start elsif)
+ . 3465)
+ ((in-block elsif "")
+ . 3465))
+ ((select-clause . 3589)
+ (nested-statement-continuation . 3588)
+ (statement-continuation . 3588))
+ ((select-table-continuation . 3619)
+ (nested-statement-continuation . 3588)
+ (statement-continuation . 3588))
+ ((select-clause . 3589)
+ (nested-statement-continuation . 3588)
+ (statement-continuation . 3588))
+ (((in-block elsif "")
+ . 3582))
+ (((in-block elsif "")
+ . 3582))
+ (((block-start else)
+ . 3582)
+ ((in-block elsif "")
+ . 3582))
+ (((in-block else "")
+ . 3752))
+ ((statement-continuation . 3763))
+ ((statement-continuation . 3763))
+ ((statement-continuation . 3763))
+ ((statement-continuation . 3763))
+ ((statement-continuation . 3763))
+ (((in-block loop "")
+ . 3763))
+ ((statement-continuation . 3921))
+ ((statement-continuation . 3921))
+ ((statement-continuation . 3921))
+ ((statement-continuation . 3921))
+ (((block-end loop "")
+ . 3763)
+ ((in-block loop "")
+ . 3763))
+ (((block-end if "")
+ . 3395)
+ ((in-block else "")
+ . 3752))
+ (((block-end nil "")
+ . 3361)
+ ((in-begin-block nil "")
+ . 3361))
+ (((block-end toplevel nil)
+ . 3311)
+ ((in-begin-block toplevel nil)
+ . 3311))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((in-begin-block toplevel nil)
+ . 4150))
+ (((in-begin-block toplevel nil)
+ . 4150))
+ (((in-begin-block toplevel nil)
+ . 4150))
+ (((in-begin-block toplevel nil)
+ . 4150))
+ (((in-begin-block nil "")
+ . 4226))
+ (((in-begin-block nil "")
+ . 4226))
+ (((in-begin-block nil "")
+ . 4226))
+ (((in-block if "")
+ . 4273))
+ (((in-block if "")
+ . 4273))
+ (((block-start elsif)
+ . 4273)
+ ((in-block if "")
+ . 4273))
+ (((in-block elsif "")
+ . 4343))
+ (((in-block elsif "")
+ . 4343))
+ (((block-start elsif)
+ . 4343)
+ ((in-block elsif "")
+ . 4343))
+ ((select-clause . 4426)
+ (nested-statement-continuation . 4425)
+ (statement-continuation . 4425))
+ (((in-select-clause "where")
+ . 4468)
+ (nested-statement-continuation . 4425)
+ (statement-continuation . 4425))
+ (((in-block elsif "")
+ . 4419))
+ (((in-block elsif "")
+ . 4419))
+ (((block-start else)
+ . 4419)
+ ((in-block elsif "")
+ . 4419))
+ (((in-block else "")
+ . 4568))
+ ((statement-continuation . 4579))
+ ((statement-continuation . 4579))
+ (((in-block loop "")
+ . 4579))
+ ((statement-continuation . 4670))
+ ((statement-continuation . 4670))
+ (((block-end loop "")
+ . 4579)
+ ((in-block loop "")
+ . 4579))
+ (((block-end if "")
+ . 4273)
+ ((in-block else "")
+ . 4568))
+ (((block-end nil "")
+ . 4226)
+ ((in-begin-block nil "")
+ . 4226))
+ (((block-end toplevel nil)
+ . 4150)
+ ((in-begin-block toplevel nil)
+ . 4150))
+ ((toplevel . 1))
+ (((block-start begin)
+ . 1)
+ (toplevel . 1))
+ ((toplevel . 1))
+ (((in-begin-block toplevel nil)
+ . 4822))
+ (((in-begin-block toplevel nil)
+ . 4822))
+ (((in-begin-block toplevel nil)
+ . 4822))
+ (((in-begin-block toplevel nil)
+ . 4822))
+ (((in-begin-block toplevel nil)
+ . 4822))
+ (((in-begin-block nil "")
+ . 4928))
+ (((in-begin-block nil "")
+ . 4928))
+ (((in-begin-block nil "")
+ . 4928))
+ (((in-begin-block nil "")
+ . 4928))
+ (((in-begin-block nil "")
+ . 4928))
+ (((in-block if "")
+ . 5047))
+ (((in-block if "")
+ . 5047))
+ (((block-start elsif)
+ . 5047)
+ ((in-block if "")
+ . 5047))
+ (((in-block elsif "")
+ . 5117))
+ (((in-block elsif "")
+ . 5117))
+ (((block-start elsif)
+ . 5117)
+ ((in-block elsif "")
+ . 5117))
+ (((in-block elsif "")
+ . 5193))
+ (((in-block elsif "")
+ . 5193))
+ (((block-start else)
+ . 5193)
+ ((in-block elsif "")
+ . 5193))
+ (((in-block else "")
+ . 5266))
+ ((nested-statement-continuation . 5303)
+ (statement-continuation . 5303))
+ ((nested-statement-continuation . 5303)
+ (statement-continuation . 5303))
+ ((nested-statement-continuation . 5303)
+ (statement-continuation . 5303))
+ ((nested-statement-continuation . 5303)
+ (statement-continuation . 5303))
+ (((in-block else "")
+ . 5266))
+ (((block-end if "")
+ . 5047)
+ ((in-block else "")
+ . 5266))
+ (((block-end nil "")
+ . 4928)
+ ((in-begin-block nil "")
+ . 4928))
+ (((block-end toplevel nil)
+ . 4822)
+ ((in-begin-block toplevel nil)
+ . 4822))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((block-start begin)
+ . 1)
+ (toplevel . 1))
+ ((toplevel . 1))
+ (((in-begin-block toplevel nil)
+ . 5627))
+ (((in-begin-block toplevel nil)
+ . 5627))
+ (((in-begin-block nil "")
+ . 5665))
+ (((in-begin-block nil "")
+ . 5665))
+ (((in-block if "")
+ . 5695))
+ (((in-block if "")
+ . 5695))
+ (((block-start elsif)
+ . 5695)
+ ((in-block if "")
+ . 5695))
+ (((in-block elsif "")
+ . 5802))
+ (((in-block elsif "")
+ . 5802))
+ (((in-block elsif "")
+ . 5802))
+ (((block-start else)
+ . 5802)
+ ((in-block elsif "")
+ . 5802))
+ (((in-block else "")
+ . 5916))
+ (((in-block else "")
+ . 5916))
+ (((block-end if "")
+ . 5695)
+ ((in-block else "")
+ . 5916))
+ (((block-end nil "")
+ . 5665)
+ ((in-begin-block nil "")
+ . 5665))
+ (((block-end toplevel nil)
+ . 5627)
+ ((in-begin-block toplevel nil)
+ . 5627))
+ ((toplevel . 1))
+ ((toplevel . 1)))
\ No newline at end of file
--- /dev/null
+/* -*- sql-product: postgres; -*- */
+set serveroutput on;
+
+begin transaction;
+do $$ DECLARE
+ v1 TABLE1.F1%TYPE;
+ v2 record;
+ BEGIN
+ v1 = :v_v1;
+ if v1 in (NULL, '') then
+ raise 'message1';
+ rollback;
+ elsif (select count(F1) from TABLE1
+ where F1 = v1) = 0 then
+ raise 'message2';
+ rollback;
+ elsif (select count(F2)
+ from TABLE1 natural join TABLE2
+ where F1 = v1) = 0 then
+ raise 'message3';
+ rollback;
+ elsif (select count(F3)
+ from TABLE3
+ natural join TABLE2
+ natural join TABLE1
+ where F1 = v1) = 0 then
+ raise 'message4';
+ rollback;
+ else
+ for v2 in
+ select *
+ from TABLE3
+ natural join TABLE1
+ natural join TABLE4
+ natural join TABLE2
+ where F1 = v1 loop
+ raise info 'message5',
+ v2.F3, v2.F2, v2.F4 || ' ' ||
+ v2.F9 || case when v2.F5 is NULL then '' else
+ ' ''' || v2.F5 || '''' end, v2.F6,
+ v2.F7, v2.F8;
+ end loop;
+ end if;
+ END; $$;
+commit;
+
+do $$ DECLARE
+ v1 TABLE4.F9%TYPE;
+ prev1 TABLE4.F4%TYPE;
+ v2 record;
+ BEGIN
+ v1 := :v_v1;
+ prev1 := :v_prev1;
+ if v1 in (NULL, '') then
+ raise 'message6';
+ rollback;
+ elsif prev1 in (NULL, '') then
+ raise 'message7';
+ rollback;
+ elsif (select count(F10)
+ from TABLE4
+ where F9 = v1
+ and F4 = prev1) = 0 then
+ raise 'message8';
+ rollback;
+ elsif (select count(F2)
+ from TABLE2
+ natural join TABLE5
+ natural join TABLE4
+ where F9 = v1
+ and F4 = prev1) = 0 then
+ raise 'message9';
+ rollback;
+ elsif (select count(F3)
+ from TABLE3
+ natural join TABLE2
+ natural join TABLE5
+ natural join TABLE4
+ where F9 = v1
+ and F4 = prev1) = 0 then
+ raise 'message10';
+ rollback;
+ else
+ for v2 in select *
+ from TABLE4
+ natural join TABLE5
+ natural join TABLE3
+ natural join TABLE2
+ where F9 = v1
+ and F4 = prev1 loop
+ raise info 'message11',
+ v2.F3, v2.F2, v2.F4 || ' ' ||
+ v2.F9 || case when v2.F5 is NULL then '' else
+ ' ''' || v2.F5 || '''' end, v2.F6,
+ v2.F7, v2.F8;
+ end loop;
+ end if;
+ end; $$;
+
+
+do $$ DECLARE
+ v2 record;
+ v3 TABLE2.F6%TYPE;
+ BEGIN
+ v3 := :v_v3;
+ if v3 in ('', NULL) then
+ raise 'message12';
+ rollback;
+ elsif (select count(F6) from TABLE2
+ where F6 = v3) = 0 then
+ raise 'message13';
+ rollback;
+ elsif (select count(F3)
+ from TABLE3
+ natural join TABLE2
+ where F6 = v3) = 0 then
+ raise 'message14';
+ rollback;
+ else
+ for v2 in select *
+ from TABLE4
+ natural join TABLE5
+ natural join TABLE3
+ natural join TABLE2
+ where F6 = v3 loop
+ raise info 'message15',
+ v2.F3, v2.F2, v2.F4 || ' ' ||
+ v2.F9 || case when v2.F5 is NULL then '' else
+ ' ''' || v2.F5 || '''' end, v2.F6,
+ v2.F7, v2.F8;
+ end loop;
+ end if;
+ END; $$;
+
+do $$ DECLARE
+ v2 record;
+ v3 TABLE2.F2%TYPE;
+ BEGIN
+ v3 := :v_ouv_titre;
+ if v3 in ('', NULL) then
+ raise 'message16';
+ rollback;
+ elsif (select count(F2) from TABLE2
+ where F2 = v3) = 0 then
+ raise 'messaeg17';
+ rollback;
+ elsif (select count(F3)
+ from TABLE3
+ natural join TABLE2
+ where F2 = v3) = 0 then
+ raise 'message18';
+ rollback;
+ else
+ for v2 in select *
+ from TABLE4
+ natural join TABLE5
+ natural join TABLE3
+ natural join TABLE2
+ where F2 = v3 loop
+ raise info 'message19',
+ v2.F3, v2.F2, v2.F4 || ' ' ||
+ v2.F9 || case when v2.F5 is NULL then '' else
+ ' ''' || v2.F5 || '''' end, v2.F6,
+ v2.F7, v2.F8;
+ end loop;
+ end if;
+ END;
+$$;
+
+do $$ DECLARE
+ v1 TABLE6.F12%TYPE;
+ prev1 TABLE6.F13%TYPE;
+ v2 record;
+ BEGIN
+ v1 := :v_v1;
+ prev1 := :v_v2;
+ if v1 in ('', NULL) then
+ raise 'message20';
+ rollback;
+ elsif prev1 in ('', NULL) then
+ raise 'message21';
+ rollback;
+ elsif (select count(F23) from TABLE6
+ where F12 = v1
+ and F13 = prev1) = 0 then
+ raise 'message22';
+ rollback;
+ else
+ for v2 in select * from TABLE6
+ where F12 = v1
+ and F13 = prev1 loop
+ raise info 'message23',
+ v2.F23, v2.F13 || ' ' || v2.F12,
+ v2.F27, v2.F28;
+ end loop;
+ end if;
+ END;
+$$;
+
+begin transaction;
+do $$ DECLARE
+ v1 TABLE6.F23%TYPE;
+ v1 TABLE6.F12%TYPE;
+ prev1 TABLE6.F13%TYPE;
+ v9 TABLE6.V2%TYPE;
+ BEGIN
+ select max(v2_id)+1 into v1 from t_v2ent;
+ v1 := :v_v2_c2;
+ prev1 := :v_v2_c8;
+ v9 := :v_v2_c9;
+ if v1 in ('', NULL) then
+ raise 'message24';
+ rollback;
+ elsif prev1 in ('', NULL) then
+ raise 'message25';
+ rollback;
+ elsif v9 in ('', NULL) then
+ raise 'message26';
+ rollback;
+ else
+ insert into TABLE6 values (v1, :v19, timestamp 'now',
+ v1, prev1, :v_v2_v8,
+ :v_v2_c7, :v_v2_v9,
+ :v_v2_c8, :v_v2_v10,
+ :v_v2_c9, v9);
+ raise notice 'message27', v1;
+ end if;
+ END;
+$$;
+commit;
+
+begin transaction;
+do $$ DECLARE
+ v1 TABLE6.F23%TYPE;
+ BEGIN
+ v1 := :v_v2_id;
+ if (select count(F23) from TABLE6 where F23 = v2_id) = 0 then
+ raise 'message28';
+ rollback;
+ elsif (select F27 from TABLE6 where F23 = v1) is not
+ NULL then
+ raise 'message29';
+ rollback;
+ else
+ delete from TABLE6 where V1 = v1;
+ raise notice 'message 30';
+ end if;
+ END;
+$$;
+commit;
--- /dev/null
+(((toplevel . 1))
+ ((select-clause . 1)
+ (statement-continuation . 1))
+ ((select-table-continuation . 12)
+ (statement-continuation . 1))
+ ((nested-statement-open . 34)
+ (statement-continuation . 34))
+ ((nested-statement-continuation . 34)
+ (statement-continuation . 34))
+ ((select-join-condition . 61)
+ (nested-statement-continuation . 34)
+ (statement-continuation . 34))
+ ((nested-statement-close . 34)
+ (statement-continuation . 34))
+ ((select-join-condition . 29)
+ (statement-continuation . 1))
+ ((toplevel . 1))
+ ((toplevel . 1)))
\ No newline at end of file
--- /dev/null
+select *
+ from t1
+ join (
+ t2
+ left join t3
+ using (k2)
+ )
+ on t1.k1 = t2.k1;
+
--- /dev/null
+(((toplevel . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ (((block-end package "my_package")
+ . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1)))
+
+
\ No newline at end of file
--- /dev/null
+(((toplevel . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ ((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ (((package "my_package")
+ . 1))
+ (((block-end package "my_package")
+ . 1)
+ ((package "my_package")
+ . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1)))
+
+
\ No newline at end of file
--- /dev/null
+create or replace package my_package authid current_user
+ -- Author : me
+ -- Date : 06/05/2017
+ -- Version : 715.00.0001
+ -- Object : sample package
+ -- ----------------------------------------------------------------------------
+
+ -------------------------------------------------------------------------------
+ -- NAME : simple_boolean_fct_1
+ -- CREATED : 06/05/2017
+ -- AUTHOR :
+ -------------------------------------------------------------------------------
+ -- RET : true if all goes well, false instead
+ -------------------------------------------------------------------------------
+ -- DESC : dummy function
+ -------------------------------------------------------------------------------
+ --
+ FUNCTION simple_boolean_fct_1(param1 in boolean)
+ RETURN BOOLEAN;
+
+ -------------------------------------------------------------------------------
+ -- NAME : simple_boolean_fct_2
+ -- CREATED : 06/05/2017
+ -- AUTHOR :
+ -------------------------------------------------------------------------------
+ -- RET : true if all goes well, false instead
+ -------------------------------------------------------------------------------
+ -- DESC : dummy function
+ -------------------------------------------------------------------------------
+ FUNCTION simple_bolean_fct_2(param1 in boolean)
+ RETURN BOOLEAN;
+
+ -------------------------------------------------------------------------------
+ -- NAME : simple_boolean_fct_3
+ -- CREATED : 06/05/2017
+ -- AUTHOR :
+ -------------------------------------------------------------------------------
+ -- RET : true if all goes well, false instead
+ -------------------------------------------------------------------------------
+ -- DESC : dummy function
+ -------------------------------------------------------------------------------
+ FUNCTION simple_boolean_fct_3(param1 in boolean)
+ RETURN BOOLEAN;
+end my_package;
+-- Local Variables:
+-- sql-product: oracle
+-- End:
--- /dev/null
+(((toplevel . 1))
+ ((select-column . 1)
+ (statement-continuation . 1))
+ ((select-clause . 1)
+ (statement-continuation . 1))
+ ((select-table-continuation . 14)
+ (statement-continuation . 1))
+ ((select-table-continuation . 14)
+ (statement-continuation . 1))
+ ((select-join-condition . 34)
+ (statement-continuation . 1))
+ ((select-join-condition . 34)
+ (statement-continuation . 1))
+ ((comment-start . 1)
+ (statement-continuation . 1))
+ ((select-table-continuation . 14)
+ (statement-continuation . 1))
+ ((nested-statement-open . 132)
+ (statement-continuation . 132))
+ ((nested-statement-continuation . 132)
+ (statement-continuation . 132))
+ ((select-join-condition . 153)
+ (nested-statement-continuation . 132)
+ (statement-continuation . 132))
+ ((select-join-condition . 153)
+ (nested-statement-continuation . 132)
+ (statement-continuation . 132))
+ ((nested-statement-close . 132)
+ (statement-continuation . 132))
+ ((select-join-condition . 127)
+ (statement-continuation . 1))
+ ((select-join-condition . 127)
+ (statement-continuation . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((select-column . 389)
+ (statement-continuation . 389))
+ ((select-clause . 389)
+ (statement-continuation . 389))
+ ((select-table-continuation . 402)
+ (statement-continuation . 389))
+ ((select-table-continuation . 402)
+ (statement-continuation . 389))
+ ((select-join-condition . 422)
+ (statement-continuation . 389))
+ ((nested-statement-continuation . 443)
+ (statement-continuation . 443))
+ ((comment-start . 389)
+ (statement-continuation . 389))
+ ((select-table-continuation . 402)
+ (statement-continuation . 389))
+ ((nested-statement-open . 606)
+ (statement-continuation . 606))
+ ((nested-statement-continuation . 606)
+ (statement-continuation . 606))
+ ((select-join-condition . 627)
+ (nested-statement-continuation . 606)
+ (statement-continuation . 606))
+ ((nested-statement-continuation . 655)
+ (statement-continuation . 655))
+ ((nested-statement-close . 606)
+ (statement-continuation . 606))
+ ((select-join-condition . 601)
+ (statement-continuation . 389))
+ ((nested-statement-continuation . 771)
+ (statement-continuation . 771))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((nested-statement-continuation . 874)
+ (statement-continuation . 874))
+ ((select-join-condition . 885)
+ (nested-statement-continuation . 874)
+ (statement-continuation . 874))
+ ((select-clause . 867)
+ (statement-continuation . 867))
+ ((toplevel . 1)))
+
\ No newline at end of file
--- /dev/null
+select
+ *
+ from
+ t1
+ join t2
+ on t1.k = t2.k
+ and t1.j = t2.j
+ -- here's a "nested join"
+ join (
+ t3
+ left join t4
+ on t3.k = t4.k
+ and t3.j = t4.j -- this "and" is a select-join-condition
+ )
+ on t1.k = t3.k
+ and t1.k = t4.k; -- this "and" is a select-join-condition
+
+select
+ *
+ from
+ t1
+ join t2
+ on (t1.k = t2.k
+ and t1.j = t2.j) -- this "and" is a nested-statement-continuation
+ -- here's a "nested join"
+ join (
+ t3
+ left join t4
+ on (t3.k = t4.k
+ and t3.j = t4.j) -- this "and" is a nested-statement-continuation
+ )
+ on (t1.k = t3.k
+ and t1.k = t4.k); -- this "and" is a nested-statement-continuation
+
+
+select (a
+ join b
+ on a.k1 = b.k2) -- this is a select-join-condition (hey! we're not an sql parser
+ from a, b;
--- /dev/null
+(((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 . 76)
+ (statement-continuation . 76))
+ ((select-clause . 76)
+ (statement-continuation . 76))
+ ((select-clause . 76)
+ (statement-continuation . 76))
+ ((select-clause . 76)
+ (statement-continuation . 76))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((select-clause . 127)
+ (statement-continuation . 127))
+ ((select-clause . 127)
+ (statement-continuation . 127))
+ ((select-clause . 127)
+ (statement-continuation . 127))
+ ((select-clause . 127)
+ (statement-continuation . 127))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((select-clause . 178)
+ (statement-continuation . 178))
+ ((select-clause . 178)
+ (statement-continuation . 178))
+ ((select-clause . 178)
+ (statement-continuation . 178))
+ ((select-clause . 178)
+ (statement-continuation . 178))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((select-clause . 233)
+ (statement-continuation . 233))
+ ((select-clause . 233)
+ (statement-continuation . 233))
+ ((select-clause . 233)
+ (statement-continuation . 233))
+ ((select-clause . 233)
+ (statement-continuation . 233))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((select-clause . 284)
+ (statement-continuation . 284))
+ ((select-clause . 284)
+ (statement-continuation . 284))
+ ((select-clause . 284)
+ (statement-continuation . 284))
+ ((select-clause . 284)
+ (statement-continuation . 284))
+ ((toplevel . 1)))
\ No newline at end of file
--- /dev/null
+select *
+ from foo
+ full join bar
+ on foo.k = bar.k;
+
+select id
+ from foo
+except
+select id
+ from bar;
+
+select id
+ from foo
+ union
+select id
+ from bar;
+
+select id
+ from foo
+ union all
+select id
+ from bar;
+
+select id
+ from foo
+ minus
+select id
+ from bar;
+
+select id
+ from foo
+intersect
+select id
+ from bar;
--- /dev/null
+(((toplevel . 1))
+ (((create-statement temp "table")
+ . 1))
+ (((create-statement temp "table")
+ . 1))
+ (((create-statement temp "table")
+ . 1))
+ (((create-statement temp "table")
+ . 1))
+ (((create-statement temp "table")
+ . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((create-statement view "myview")
+ . 196))
+ ((select-column . 233)
+ ((create-statement view "myview")
+ . 196))
+ ((select-clause . 233)
+ ((create-statement view "myview")
+ . 196))
+ ((select-table-continuation . 329)
+ ((create-statement view "myview")
+ . 196))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((create-statement table "foo")
+ . 403))
+ (((create-statement table "foo")
+ . 403))
+ (((create-statement table "foo")
+ . 403))
+ ((select-column . 525)
+ ((create-statement table "foo")
+ . 403))
+ ((select-clause . 525)
+ ((create-statement table "foo")
+ . 403))
+ ((select-table-continuation . 621)
+ ((create-statement table "foo")
+ . 403))
+ ((toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1)))
\ No newline at end of file
--- /dev/null
+create or replace
+ private temp table mytable as
+ select distinct table1.fielda as firstfield,
+ table2.fieldb as secondfield
+ from table1
+ join table2 on table1.table1id = table2.fktable1;
+
+create materialized view myview as
+ select distinct table1.fielda as firstfield,
+ table2.fieldb as secondfield
+ from table1
+ join table2 on table1.table1id = table2.fktable1;
+
+create private -- apparently all temporary tables must be private in Oracle
+ temporary table if not exists foo
+ as
+ select distinct table1.fielda as firstfield,
+ table2.fieldb as secondfield
+ from table1
+ join table2 on table1.table1id = table2.fktable1;
+
+-- local variables:
+-- mode: sql
+-- sql-product: oracle
+-- end:
--- /dev/null
+(((toplevel . 1))
+ (((create-statement table "mytable")
+ . 1))
+ (((create-statement table "mytable")
+ . 1))
+ ((select-column . 52)
+ ((create-statement table "mytable")
+ . 1))
+ ((select-clause . 52)
+ ((create-statement table "mytable")
+ . 1))
+ ((select-table-continuation . 148)
+ ((create-statement table "mytable")
+ . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((create-statement table "mytable")
+ . 222))
+ ((select-column . 273)
+ ((create-statement table "mytable")
+ . 222))
+ ((select-clause . 273)
+ ((create-statement table "mytable")
+ . 222))
+ ((select-table-continuation . 369)
+ ((create-statement table "mytable")
+ . 222))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((create-statement view "myview")
+ . 443))
+ ((select-column . 480)
+ ((create-statement view "myview")
+ . 443))
+ ((select-clause . 480)
+ ((create-statement view "myview")
+ . 443))
+ ((select-table-continuation . 576)
+ ((create-statement view "myview")
+ . 443))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((create-statement table "foo")
+ . 650))
+ (((create-statement table "foo")
+ . 650))
+ ((select-column . 698)
+ ((create-statement table "foo")
+ . 650))
+ ((select-clause . 698)
+ ((create-statement table "foo")
+ . 650))
+ ((select-table-continuation . 794)
+ ((create-statement table "foo")
+ . 650))
+ ((toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1)))
+
+
\ No newline at end of file
--- /dev/null
+create or replace
+ global temp table mytable as
+ select distinct table1.fielda as firstfield,
+ table2.fieldb as secondfield
+ from table1
+ join table2 on table1.table1id = table2.fktable1;
+
+create local temporary unlogged table mytable as
+ select distinct table1.fielda as firstfield,
+ table2.fieldb as secondfield
+ from table1
+ join table2 on table1.table1id = table2.fktable1;
+
+create materialized view myview as
+ select distinct table1.fielda as firstfield,
+ table2.fieldb as secondfield
+ from table1
+ join table2 on table1.table1id = table2.fktable1;
+
+create temporary table if not exists foo
+ as
+ select distinct table1.fielda as firstfield,
+ table2.fieldb as secondfield
+ from table1
+ join table2 on table1.table1id = table2.fktable1;
+
+-- local variables:
+-- mode: sql
+-- sql-product: postgres
+-- end:
--- /dev/null
+(((toplevel . 1))
+ ((toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((comment-start . 1)
+ (toplevel . 1))
+ ((toplevel . 1)))
--- /dev/null
+drop function if exists test;
+
+-- local variables:
+-- mode: sql
+-- sql-product: postgres
+-- end:
--- /dev/null
+(((toplevel . 1))
+ ((select-clause . 5)
+ (nested-statement-continuation . 4)
+ (statement-continuation . 4))
+ ((select-clause . 5)
+ (nested-statement-continuation . 4)
+ (statement-continuation . 4))
+ (((block-start then)
+ . 1)
+ ((in-block if "")
+ . 1))
+ (((in-block then "")
+ . 70))
+ (((block-start elsif)
+ . 70)
+ ((in-block then "")
+ . 70))
+ (((in-block elsif "")
+ . 97))
+ (((block-end if "")
+ . 70)
+ ((in-block elsif "")
+ . 97))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((select-clause . 139)
+ (nested-statement-continuation . 138)
+ (statement-continuation . 138))
+ ((select-clause . 139)
+ (nested-statement-continuation . 138)
+ (statement-continuation . 138))
+ (((block-start then)
+ . 135)
+ ((in-block if "")
+ . 135))
+ (((in-block then "")
+ . 204))
+ (((block-start elseif)
+ . 204)
+ ((in-block then "")
+ . 204))
+ (((in-block else "")
+ . 231))
+ (((block-end if "")
+ . 204)
+ ((in-block else "")
+ . 231))
+ ((toplevel . 1)))
\ No newline at end of file
--- /dev/null
+IF (SELECT ID
+ FROM SomeTable
+ WHERE SomeField > 0) IS NULL
+THEN
+ SET SomeVar = TRUE;
+ELSIF
+ SET SomeVar = FALSE;
+END IF;
+
+IF (SELECT ID
+ FROM SomeTable
+ WHERE SomeField > 0) IS NULL
+THEN
+ SET SomeVar = TRUE;
+ELSEIF
+ SET SomeVar = FALSE;
+END IF;
--- /dev/null
+(((toplevel . 1))
+ ((select-column . 1)
+ (statement-continuation . 1))
+ ((select-column . 1)
+ (statement-continuation . 1))
+ ((comment-start . 1)
+ (statement-continuation . 1))
+ ((select-column . 1)
+ (statement-continuation . 1))
+ ((select-column . 1)
+ (statement-continuation . 1))
+ ((select-clause . 1)
+ (statement-continuation . 1))
+ ((toplevel . 1)))
+
\ No newline at end of file
--- /dev/null
+SELECT
+ Column1,
+ Column2,
+ -- this is FROM a blog WHERE useful stuff was described
+ Column3,
+ Column4
+ FROM Atable;
--- /dev/null
+((((block-start begin)
+ . 1)
+ (toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((block-start begin)
+ . 1)
+ (toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((block-start begin)
+ . 1)
+ (toplevel . 1))
+ ((statement-continuation . 76))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ (((block-start begin)
+ . 1)
+ (toplevel . 1))
+ ((statement-continuation . 163))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1))
+ ((toplevel . 1)))
+
\ No newline at end of file
--- /dev/null
+begin;
+ select * from foo;
+commit;
+
+begin work;
+select * from foo;
+commit;
+
+begin -- a comment
+ work;
+select * from foo;
+commit;
+
+begin -- a comment
+ transaction;
+select * from foo;
+commit;
+
--- /dev/null
+(((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
--- /dev/null
+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;