blob: c98653708fbbb76e30c1c0a201e2f4f02e1eecde [file] [log] [blame]
/* Copyright (c) 2001-2010, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* @(#)$Id: sqltool.flex 3481 2010-02-26 18:05:06Z fredt $ */
package org.hsqldb.cmdline.sqltool;
import java.io.PrintStream;
import org.hsqldb.lib.FrameworkLogger;
%%
// Defaults to Yylex
%class SqlFileScanner
%implements TokenSource
%{
static private FrameworkLogger logger =
FrameworkLogger.getLog(SqlFileScanner.class);
private StringBuffer commandBuffer = new StringBuffer();
private boolean interactive;
private PrintStream psStd = System.out;
private String magicPrefix;
private int requestedState = YYINITIAL;
private String rawLeadinPrompt;
private boolean specialAppendState;
// This last is needed for very unique check needed when appending to
// a SQL command. Only applies to interactive mode.
public void setRequestedState(int requestedState) {
this.requestedState = requestedState;
}
/**
* Really need a way to validate that this is called before using the
* scanner, like Spring's init-method property.
* For now, will just check explicitly before using.
*/
public void setRawLeadinPrompt(String rawLeadinPrompt) {
this.rawLeadinPrompt = rawLeadinPrompt;
}
private void rawLeadinPrompt() {
if (!interactive) {
return;
}
if (rawLeadinPrompt == null) {
throw new RuntimeException("Internal assertion failed. "
+ "Scanner's message Resource Bundle not initialized properly");
}
psStd.println(rawLeadinPrompt);
}
// Trims only the end
private void trimBuffer() {
int len = commandBuffer.length();
commandBuffer.setLength(len -
((len > 1 && commandBuffer.charAt(len - 2) == '\r') ? 2 : 1));
}
public void setCommandBuffer(String s) {
commandBuffer.setLength(0);
commandBuffer.append(s);
}
public void setInteractive(boolean interactive) {
this.interactive = interactive;
}
public void setMagicPrefix(String magicPrefix) {
this.magicPrefix = magicPrefix;
}
public void setStdPrintStream(PrintStream psStd) {
this.psStd = psStd;
}
//private String sqlPrompt = "+sql> ";
private String sqlPrompt = null;
public void setSqlPrompt(String sqlPrompt)
{
this.sqlPrompt = sqlPrompt;
}
public String getSqlPrompt() {
return sqlPrompt;
}
//private String sqltoolPrompt = "sql> ";
private String sqltoolPrompt = null;
public void setSqltoolPrompt(String sqltoolPrompt)
{
this.sqltoolPrompt = sqltoolPrompt;
}
public String getSqltoolPrompt() {
return sqltoolPrompt;
}
//private String rawPrompt = "raw> ";
private String rawPrompt = null;
public void setRawPrompt(String rawPrompt)
{
this.rawPrompt = rawPrompt;
}
public String getRawPrompt() {
return rawPrompt;
}
private void debug(String id, String msg) {
logger.finest(id + ": [" + msg + ']');
}
public String strippedYytext() {
String lineString = yytext();
int len = lineString.length();
len = len - ((len > 1 && lineString.charAt(len - 2) == '\r') ? 2 : 1);
return (lineString.substring(0, len));
}
// Trims only the end
public void pushbackTrim() {
String lineString = yytext();
int len = lineString.length();
yypushback((len > 1 && lineString.charAt(len - 2) == '\r') ? 2 : 1);
}
private void prompt(String s) {
if (!interactive) return;
psStd.print(s);
}
public void prompt() {
if (sqltoolPrompt != null) prompt(sqltoolPrompt);
specialAppendState = (interactive && magicPrefix != null);
// This tells scanner that if SQL input "looks" empty, it isn't.
if (interactive && magicPrefix != null) {
psStd.print(magicPrefix);
magicPrefix = null;
}
}
%}
%public
//%int
%line
%column
%eofclose
%unicode
%type Token
%xstates SQL RAW SQL_SINGLE_QUOTED SQL_DOUBLE_QUOTED GOBBLE SPECIAL PL EDIT
%xstates MACRO PROMPT_CHANGE_STATE
/* Single-quotes Escaped with '',
* In Oracle, at least, no inner double-quotes (i.e. no escaping)
* SQL-Embedded comments are passed to SQL engine as part of SQL command */
/* Expressions could be simplified by using "." instead of "[^\r\n]", but
* the JFlex docs say that "." means "[^n]", therefore this would mess up
* DOS-style line endings. */
LINETERM_MAC = \r|\n|\r\n
SQL_STARTER = [^\n\r\t\f \\*:\"\']
TRADITIONAL_COMMENT = "/*" ~"*/"
/* CURLY_COMMENT = "{" ~"}" Purposefully not supporting */
%%
<PROMPT_CHANGE_STATE> {LINETERM_MAC} {
yybegin(requestedState);
prompt();
}
<GOBBLE> ~{LINETERM_MAC} {
yybegin(YYINITIAL);
debug("Gobbled", yytext());
prompt();
}
<SQL, SQL_SINGLE_QUOTED, SQL_DOUBLE_QUOTED, SPECIAL, PL, EDIT, MACRO> <<EOF>> {
yybegin(YYINITIAL);
return new Token(Token.UNTERM_TYPE, commandBuffer, yyline);
}
{TRADITIONAL_COMMENT} { /* Ignore top-level traditional comments */
debug ("/**/ Comment", yytext());
}
[ \f\t]+ { /* Ignore top-level whte space */
debug("Whitespace", yytext());
}
{LINETERM_MAC} {
prompt();
}
[--][^\n\r]* {
debug ("-- Comment", yytext());
}
; {
return new Token(Token.SQL_TYPE, yyline);
}
[Bb][Ee][Gg][Ii][Nn] [\f\t ]* {LINETERM_MAC} |
[Cc][Rr][Ee][Aa][Tt][Ee] [\f\t ]+ [Ff][Uu][Nn][Cc][Tt][Ii][Oo][Nn] [^\n\r]* {LINETERM_MAC} |
[Cc][Rr][Ee][Aa][Tt][Ee] [\f\t ]+ [Pp][Rr][Oo][Cc][Ee][Dd][Uu][Rr][Ee] [^\n\r]* {LINETERM_MAC} |
[Dd][Ee][Cc][Ll][Aa][Rr][Ee] [\f\t ]* {LINETERM_MAC} {
/* These are commands which may contain nested commands and/or which
* require the closing semicolon to sent to the DB engine.
* The BEGIN and DECLARE needed for PL/SQL probably do not need to
* terminate the line, as we have it specified here, but I'd rather not be
* too liberal with proprietary SQL like this, because it's easy to
* envision other proprietary or non-proprietary commands beginning with
* DECLARE or BEGIN. */
setCommandBuffer(strippedYytext());
yybegin(RAW);
rawLeadinPrompt();
if (rawPrompt != null) prompt(rawPrompt);
}
\* {
commandBuffer.setLength(0);
yybegin(PL);
}
\\ {
commandBuffer.setLength(0);
yybegin(SPECIAL);
}
\: {
commandBuffer.setLength(0);
yybegin(EDIT);
}
\/ {
commandBuffer.setLength(0);
yybegin(MACRO);
}
<RAW> {
[\f\t ]*\.[\f\t ]* ; [\f\t ]* {LINETERM_MAC} {
yybegin(YYINITIAL);
prompt();
return new Token(Token.RAWEXEC_TYPE, commandBuffer, yyline);
}
[\f\t ]*\.[\f\t ]* {LINETERM_MAC} {
yybegin(YYINITIAL);
prompt();
return new Token(Token.RAW_TYPE, commandBuffer, yyline);
}
~{LINETERM_MAC} {
if (commandBuffer.length() > 0) commandBuffer.append('\n');
commandBuffer.append(strippedYytext());
if (rawPrompt != null) prompt(rawPrompt);
}
}
<SPECIAL> {LINETERM_MAC} {
if (commandBuffer.toString().trim().equals(".")) {
commandBuffer.setLength(0);
yybegin(RAW);
rawLeadinPrompt();
if (rawPrompt != null) prompt(rawPrompt);
} else {
requestedState = YYINITIAL;
yybegin(PROMPT_CHANGE_STATE);
pushbackTrim();
return new Token(Token.SPECIAL_TYPE, commandBuffer, yyline);
}
}
<PL> {LINETERM_MAC} {
requestedState = YYINITIAL;
yybegin(PROMPT_CHANGE_STATE);
pushbackTrim();
return new Token(Token.PL_TYPE, commandBuffer, yyline);
}
<MACRO> {LINETERM_MAC} {
requestedState = YYINITIAL;
yybegin(PROMPT_CHANGE_STATE);
pushbackTrim();
return new Token(Token.MACRO_TYPE, commandBuffer, yyline);
}
<EDIT> {LINETERM_MAC} {
requestedState = YYINITIAL;
yybegin(PROMPT_CHANGE_STATE);
pushbackTrim();
return new Token(Token.EDIT_TYPE, commandBuffer, yyline);
}
<SPECIAL, PL, MACRO> {
// Purposefully not allowing comments within :Edit commands
{TRADITIONAL_COMMENT} {
/* embedded comment may disable opening closing \n */
debug("Spl. /**/ Comment", yytext());
}
"--" ~{LINETERM_MAC} {
pushbackTrim();
/* embedded comment may disable opening quotes and closing ; */
debug("Spl. -- Comment", yytext());
}
}
<SPECIAL, EDIT, PL, MACRO> {
[^\n\r] {
commandBuffer.append(yytext());
}
}
{SQL_STARTER} {
setCommandBuffer(yytext());
yybegin(SQL);
}
<SQL> {
^[\f\t ]* {LINETERM_MAC} {
if (interactive && !specialAppendState) {
requestedState = YYINITIAL;
yybegin(PROMPT_CHANGE_STATE);
pushbackTrim();
trimBuffer();
return new Token(Token.BUFFER_TYPE, commandBuffer, yyline);
}
specialAppendState = false;
commandBuffer.append(yytext());
}
{TRADITIONAL_COMMENT} {
specialAppendState = false;
commandBuffer.append(yytext());
/* embedded comment may disable opening quotes and closing ; */
debug("SQL /**/ Comment", yytext());
}
"--" ~{LINETERM_MAC} {
specialAppendState = false;
commandBuffer.append(yytext());
/* embedded comment may disable opening quotes and closing ; */
debug("SQL -- Comment", yytext());
}
{LINETERM_MAC} {
specialAppendState = false;
commandBuffer.append(yytext());
if (sqlPrompt != null) prompt(sqlPrompt);
}
[^\"\';] {
specialAppendState = false;
commandBuffer.append(yytext());
}
\' {
specialAppendState = false;
commandBuffer.append(yytext());
yybegin(SQL_SINGLE_QUOTED);
}
\" {
specialAppendState = false;
commandBuffer.append(yytext());
yybegin(SQL_DOUBLE_QUOTED);
}
; {
specialAppendState = false;
yybegin(YYINITIAL);
return new Token(Token.SQL_TYPE, commandBuffer, yyline);
}
}
<SQL_SINGLE_QUOTED> {
[^\']+ {
commandBuffer.append(yytext());
}
\'\' {
commandBuffer.append(yytext());
}
\' {
commandBuffer.append(yytext());
debug("SQL '", yytext());
yybegin(SQL);
}
}
<SQL_DOUBLE_QUOTED> {
[^\"]+ {
commandBuffer.append(yytext());
}
\" {
commandBuffer.append(yytext());
yybegin(SQL);
debug("SQL \"", yytext());
}
}
[^\r\n] {
yybegin(GOBBLE);
return new Token(Token.SYNTAX_ERR_TYPE, yytext(), yyline);
}