Author: jolee
Date: 2014-08-26 09:46:27 -0400 (Tue, 26 Aug 2014)
New Revision: 4653
Added:
branches/7.7.x/runtime/src/test/java/TestScriptReader/
branches/7.7.x/runtime/src/test/java/TestScriptReader/TestScriptReader.java
Modified:
branches/7.7.x/runtime/src/main/java/org/teiid/odbc/ScriptReader.java
Log:
TEIID-2985 allowing the script reader to ignore extra statement
delimiters
Modified: branches/7.7.x/runtime/src/main/java/org/teiid/odbc/ScriptReader.java
===================================================================
--- branches/7.7.x/runtime/src/main/java/org/teiid/odbc/ScriptReader.java 2014-08-26
13:46:21 UTC (rev 4652)
+++ branches/7.7.x/runtime/src/main/java/org/teiid/odbc/ScriptReader.java 2014-08-26
13:46:27 UTC (rev 4653)
@@ -8,7 +8,7 @@
import java.io.IOException;
import java.io.Reader;
-import java.util.Arrays;
+import java.io.StringReader;
/**
* This class can split SQL scripts to single SQL statements.
@@ -17,17 +17,13 @@
*/
public class ScriptReader {
private Reader reader;
- private char[] buffer;
- private int bufferPos;
- private int bufferStart = -1;
- private int bufferEnd;
+ private StringBuilder builder;
private boolean endOfFile;
private boolean insideRemark;
private boolean blockRemark;
- private boolean skipRemarks;
- private int remarkStart;
-
- private static int IO_BUFFER_SIZE = 4096;
+ private boolean rewrite;
+ private int expressionStart=-1;
+ private int expressionEnd=-1;
/**
* Create a new SQL script reader from the given reader
@@ -36,9 +32,14 @@
*/
public ScriptReader(Reader reader) {
this.reader = reader;
- buffer = new char[IO_BUFFER_SIZE * 2];
+ this.builder = new StringBuilder(1<<13);
}
-
+
+ public ScriptReader(String string) {
+ this.reader = new StringReader(string);
+ this.builder = new StringBuilder(string.length());
+ }
+
/**
* Close the underlying reader.
*/
@@ -56,26 +57,29 @@
if (endOfFile) {
return null;
}
- return readStatementLoop();
+ while (true) {
+ String result = readStatementLoop();
+ if (result != null || endOfFile) {
+ return result;
+ }
+ }
+
}
private String readStatementLoop() throws IOException {
- bufferStart = bufferPos;
int c = read();
while (true) {
if (c < 0) {
endOfFile = true;
- if (bufferPos - 1 == bufferStart) {
- return null;
- }
break;
} else if (c == ';') {
+ builder.setLength(builder.length()-1);
break;
}
switch (c) {
case '$': {
c = read();
- if (c == '$' && (bufferPos - bufferStart < 3 ||
buffer[bufferPos - 3] <= ' ')) {
+ if (c == '$' && (builder.length() < 3 ||
builder.charAt(builder.length() - 3) <= ' ')) {
// dollar quoted string
while (true) {
c = read();
@@ -97,15 +101,18 @@
break;
}
case '\'':
+ //TODO: in rewrite mode could handle the E' logic here rather than in
the parser
+ //however the parser now uses E' with like to detect pg specific
handling
+ if (expressionEnd != builder.length() - 1) {
+ expressionStart = builder.length() - 1;
+ }
while (true) {
c = read();
- if (c < 0) {
+ if (c < 0 || c == '\'') {
break;
}
- if (c == '\'') {
- break;
- }
}
+ expressionEnd = builder.length();
c = read();
break;
case '"':
@@ -133,7 +140,6 @@
if (c == '*') {
c = read();
if (c < 0) {
- clearRemark();
break;
}
if (c == '/') {
@@ -149,7 +155,6 @@
while (true) {
c = read();
if (c < 0) {
- clearRemark();
break;
}
if (c == '\r' || c == '\n') {
@@ -169,7 +174,6 @@
while (true) {
c = read();
if (c < 0) {
- clearRemark();
break;
}
if (c == '\r' || c == '\n') {
@@ -181,67 +185,122 @@
}
break;
}
+ case ':': {
+ if (rewrite) {
+ int start = builder.length();
+ c = read();
+ if (c == ':') {
+ while (true) {
+ c = read();
+ if (c < 0 || !Character.isLetterOrDigit(c)) {
+ String type = builder.substring(start+1, builder.length() -
(c<0?0:1));
+ builder.setLength(start-1);
+ if (expressionStart != -1 && expressionEnd == start
-1) {
+ //special handling for regclass cast - it won't always
work
+ if ("regclass".equalsIgnoreCase(type)) {
//$NON-NLS-1$
+ String name = builder.substring(expressionStart);
+ if (name.startsWith("'\"") &&
name.length() > 4) { //$NON-NLS-1$
+ name = name.substring(2, name.length()-2);
+ name = '\''+ name + '\'';
+ }
+ if (name.startsWith("'")) { //$NON-NLS-1$
+ builder.setLength(expressionStart);
+ builder.append(name.toUpperCase());
+ }
+ builder.insert(expressionStart, "(SELECT oid FROM
pg_class WHERE upper(relname) = "); //$NON-NLS-1$
+ builder.append(")"); //$NON-NLS-1$
+ } else if ("regproc".equalsIgnoreCase(type)) {
+ String name = builder.substring(expressionStart);
+ if (name.startsWith("'\"") &&
name.length() > 4) { //$NON-NLS-1$
+ name = name.substring(2, name.length()-2);
+ name = '\''+ name + '\'';
+ }
+ if (name.startsWith("'")) { //$NON-NLS-1$
+ builder.setLength(expressionStart);
+ builder.append(name.toUpperCase());
+ }
+ builder.insert(expressionStart, "(SELECT oid FROM
pg_proc WHERE upper(proname) = "); //$NON-NLS-1$
+ builder.append(")"); //$NON-NLS-1$
+ } else {
+ builder.insert(expressionStart, "cast(");
//$NON-NLS-1$
+ builder.append(" AS
").append(type).append(")"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ if (c != -1) {
+ builder.append((char)c);
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ c = read();
+ break;
+ }
+ case '~': {
+ if (rewrite) {
+ int start = builder.length() - 1;
+ boolean not = false;
+ if (start > 0 && builder.charAt(start - 1) == '!') {
+ not = true;
+ start -= 1;
+ }
+ c = read();
+ boolean like = false;
+ if (c == '~') {
+ like = true;
+ c = read();
+ }
+ if (c == '*') {
+ break; //can't handle
+ }
+ builder.setLength(start);
+ if (not) {
+ builder.append(" NOT"); //$NON-NLS-1$
+ }
+ if (like) {
+ builder.append(" LIKE "); //$NON-NLS-1$
+ } else {
+ builder.append(" LIKE_REGEX "); //$NON-NLS-1$
+ }
+ if (c != -1) {
+ builder.append((char)c);
+ }
+ }
+ c = read();
+ break;
+ }
default: {
c = read();
}
}
}
- return new String(buffer, bufferStart, bufferPos - 1 - bufferStart);
+ String result = builder.toString();
+ builder.setLength(0);
+ if (result.length() == 0) {
+ return null;
+ }
+ return result;
}
private void startRemark(boolean block) {
blockRemark = block;
- remarkStart = bufferPos - 2;
insideRemark = true;
}
private void endRemark() {
- clearRemark();
insideRemark = false;
}
- private void clearRemark() {
- if (skipRemarks) {
- Arrays.fill(buffer, remarkStart, bufferPos, ' ');
- }
- }
-
private int read() throws IOException {
- if (bufferPos >= bufferEnd) {
- return readBuffer();
- }
- return buffer[bufferPos++];
+ int c = reader.read();
+ if (c != -1) {
+ builder.append((char)c);
+ }
+ return c;
}
- private int readBuffer() throws IOException {
- if (endOfFile) {
- return -1;
- }
- int keep = bufferPos - bufferStart;
- if (keep > 0) {
- char[] src = buffer;
- if (keep + IO_BUFFER_SIZE > src.length) {
- buffer = new char[src.length * 2];
- }
- System.arraycopy(src, bufferStart, buffer, 0, keep);
- }
- remarkStart -= bufferStart;
- bufferStart = 0;
- bufferPos = keep;
- int len = reader.read(buffer, keep, IO_BUFFER_SIZE);
- if (len == -1) {
- // ensure bufferPos > bufferEnd
- bufferEnd = -1024;
- endOfFile = true;
- // ensure the right number of characters are read
- // in case the input buffer is still used
- bufferPos++;
- return -1;
- }
- bufferEnd = keep + len;
- return buffer[bufferPos++];
- }
-
/**
* Check if this is the last statement, and if the single line or block
* comment is not finished yet.
@@ -262,13 +321,8 @@
return blockRemark;
}
- /**
- * If comments should be skipped completely by this reader.
- *
- * @param skipRemarks true if comments should be skipped
- */
- public void setSkipRemarks(boolean skipRemarks) {
- this.skipRemarks = skipRemarks;
- }
+ public void setRewrite(boolean rewrite) {
+ this.rewrite = rewrite;
+ }
}
Added: branches/7.7.x/runtime/src/test/java/TestScriptReader/TestScriptReader.java
===================================================================
--- branches/7.7.x/runtime/src/test/java/TestScriptReader/TestScriptReader.java
(rev 0)
+++ branches/7.7.x/runtime/src/test/java/TestScriptReader/TestScriptReader.java 2014-08-26
13:46:27 UTC (rev 4653)
@@ -0,0 +1,95 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright (C) 2008 Red Hat, Inc.
+ * Licensed to Red Hat, Inc. under one or more contributor
+ * license agreements. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+package TestScriptReader;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.teiid.odbc.ScriptReader;
+
+@SuppressWarnings("nls")
+public class TestScriptReader {
+
+ @Test public void testRewrite() throws Exception {
+ ScriptReader sr = new ScriptReader("select 'a'::b from foo");
+ sr.setRewrite(true);
+ String result = sr.readStatement();
+ assertEquals("select cast('a' AS b) from foo", result);
+ }
+
+ @Test public void testRewriteComplexLiteral() throws Exception {
+ ScriptReader sr = new ScriptReader("select 'a''c'::b");
+ sr.setRewrite(true);
+ String result = sr.readStatement();
+ assertEquals("select cast('a''c' AS b)", result);
+ }
+
+ @Test public void testRewrite1() throws Exception {
+ ScriptReader sr = new ScriptReader("select a~b, a!~~c from foo");
+ sr.setRewrite(true);
+ String result = sr.readStatement();
+ assertEquals("select a LIKE_REGEX b, a NOT LIKE c from foo", result);
+ }
+
+ @Test public void testRewrite2() throws Exception {
+ ScriptReader sr = new ScriptReader("select a~");
+ sr.setRewrite(true);
+ String result = sr.readStatement();
+ assertEquals("select a LIKE_REGEX ", result);
+ }
+
+ @Test public void testRewrite3() throws Exception {
+ ScriptReader sr = new ScriptReader("select a::b");
+ sr.setRewrite(true);
+ String result = sr.readStatement();
+ assertEquals("select a", result);
+ }
+
+ @Test public void testDelimited() throws Exception {
+ ScriptReader sr = new ScriptReader("set foo 'bar'; set foo1
'bar1'");
+ String result = sr.readStatement();
+ assertEquals("set foo 'bar'", result);
+ result = sr.readStatement();
+ assertEquals(" set foo1 'bar1'", result);
+ }
+
+ @Test public void testRegClassCast() throws Exception {
+ ScriptReader sr = new ScriptReader("where
oid='\"a\"'::regclass");
+ sr.setRewrite(true);;
+ String result = sr.readStatement();
+ assertEquals("where oid=(SELECT oid FROM pg_class WHERE upper(relname) =
'A')", result);
+ }
+
+ @Test public void testExtraDelim() throws Exception {
+ ScriptReader sr = new ScriptReader("BEGIN;declare \"SQL_CUR0x1e4ba50\"
cursor with hold for select * from pg_proc;;fetch 1 in
\"SQL_CUR0x1e4ba50\"");
+ String result = sr.readStatement();
+ assertEquals("BEGIN", result);
+ result = sr.readStatement();
+ assertEquals("declare \"SQL_CUR0x1e4ba50\" cursor with hold for select *
from pg_proc", result);
+ result = sr.readStatement();
+ assertEquals("fetch 1 in \"SQL_CUR0x1e4ba50\"", result);
+ assertNull(sr.readStatement());
+ }
+
+}