| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.ignite.internal.processors.odbc; |
| |
| import java.util.concurrent.Callable; |
| import org.apache.ignite.IgniteException; |
| import org.apache.ignite.internal.processors.odbc.odbc.escape.OdbcEscapeUtils; |
| import org.apache.ignite.testframework.GridTestUtils; |
| import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; |
| import org.junit.Test; |
| |
| /** |
| * Escape sequence parser tests. |
| */ |
| public class OdbcEscapeSequenceSelfTest extends GridCommonAbstractTest { |
| /** |
| * Test simple cases. |
| */ |
| @Test |
| public void testTrivial() { |
| check( |
| "select * from table;", |
| "select * from table;" |
| ); |
| } |
| |
| /** |
| * Test escape sequence series. |
| */ |
| @Test |
| public void testSimpleFunction() throws Exception { |
| check( |
| "test()", |
| "{fn test()}" |
| ); |
| |
| check( |
| "select test()", |
| "select {fn test()}" |
| ); |
| |
| check( |
| "select test() from table;", |
| "select {fn test()} from table;" |
| ); |
| |
| check( |
| "func(field1) func(field2)", |
| "{fn func(field1)} {fn func(field2)}" |
| ); |
| |
| check( |
| "select func(field1), func(field2)", |
| "select {fn func(field1)}, {fn func(field2)}" |
| ); |
| |
| check( |
| "select func(field1), func(field2) from table;", |
| "select {fn func(field1)}, {fn func(field2)} from table;" |
| ); |
| } |
| |
| /** |
| * Test escape sequence for explicit data type conversion |
| */ |
| @Test |
| public void testConvertFunction() throws Exception { |
| check( |
| "CONVERT ( CURDATE(), CHAR )", |
| "{ fn CONVERT ( { fn CURDATE() }, SQL_CHAR ) }" |
| ); |
| |
| check( |
| "conVerT ( some_expression('one', 'two') , DECIMAL ( 5 , 2 ) )", |
| "{ fn conVerT ( some_expression('one', 'two') , SQL_DECIMAL ( 5 , 2 ) ) }" |
| ); |
| |
| check( |
| "convert(field,CHAR)", |
| "{fn convert(field,sql_char)}" |
| ); |
| |
| check( |
| "convert(field,BIGINT)", |
| "{fn convert(field,sql_bigint)}" |
| ); |
| |
| check( |
| "convert(field,BINARY)", |
| "{fn convert(field,sql_binary)}" // also sql_varbinary,sql_longvarbinary |
| ); |
| |
| check( |
| "convert(field,BIT)", |
| "{fn convert(field,sql_bit)}" |
| ); |
| |
| check( |
| "convert(field,CHAR(100))", |
| "{fn convert(field,sql_char(100))}" |
| ); |
| |
| check( |
| "convert(field,DECIMAL(5,2))", |
| "{fn convert(field,sql_decimal(5,2))}" // also sql_numeric |
| ); |
| |
| check( |
| "convert(field,VARCHAR(100))", |
| "{fn convert(field,sql_varchar(100))}" // also sql_longvarchar,sql_wchar,sql_wlongvarchar,sql_wvarchar |
| ); |
| |
| check( |
| "convert(field,DOUBLE)", |
| "{fn convert(field,sql_double)}" // also sql_float |
| ); |
| |
| check( |
| "convert(field,REAL)", |
| "{fn convert(field,sql_real)}" |
| ); |
| |
| check( |
| "convert(field,UUID)", |
| "{fn convert(field,sql_guid)}" |
| ); |
| |
| check( |
| "convert(field,SMALLINT)", |
| "{fn convert(field,sql_smallint)}" |
| ); |
| |
| check( |
| "convert(field,INTEGER)", |
| "{fn convert(field,sql_integer)}" |
| ); |
| |
| check( |
| "convert(field,DATE)", |
| "{fn convert(field,sql_date)}" |
| ); |
| |
| check( |
| "convert(field,TIME)", |
| "{fn convert(field,sql_time)}" |
| ); |
| |
| check( |
| "convert(field,TIMESTAMP)", |
| "{fn convert(field,sql_timestamp)}" |
| ); |
| |
| check( |
| "convert(field,TINYINT)", |
| "{fn convert(field,sql_tinyint)}" |
| ); |
| |
| //invalid odbc type |
| checkFail("{fn convert(field,char)}"); |
| |
| //no support for interval types |
| checkFail("{fn convert(field,sql_interval_second)}"); |
| checkFail("{fn convert(field,sql_interval_minute)}"); |
| checkFail("{fn convert(field,sql_interval_hour)}"); |
| checkFail("{fn convert(field,sql_interval_day)}"); |
| checkFail("{fn convert(field,sql_interval_month)}"); |
| checkFail("{fn convert(field,sql_interval_year)}"); |
| checkFail("{fn convert(field,sql_interval_year_to_month)}"); |
| checkFail("{fn convert(field,sql_interval_hour_to_minute)}"); |
| checkFail("{fn convert(field,sql_interval_hour_to_second)}"); |
| checkFail("{fn convert(field,sql_interval_minute_to_second)}"); |
| checkFail("{fn convert(field,sql_interval_day_to_hour)}"); |
| checkFail("{fn convert(field,sql_interval_day_to_minute)}"); |
| checkFail("{fn convert(field,sql_interval_day_to_second)}"); |
| |
| //failure: expected function name |
| checkFail("{fn }"); |
| |
| //failure: expected function parameter list |
| checkFail("{fn convert}"); |
| |
| //failure: expected data type parameter for convert function |
| checkFail("{fn convert ( justoneparam ) }"); |
| |
| //failure: empty precision/scale |
| checkFail("{fn convert ( justoneparam, sql_decimal( ) }"); |
| |
| //failure: empty precision/scale |
| checkFail("{fn convert ( justoneparam, sql_decimal(not_a_number) }"); |
| |
| //failure: missing scale after comma |
| checkFail("{fn convert ( justoneparam, sql_decimal(10,) }"); |
| } |
| |
| /** |
| * Test simple nested escape sequences. Depth = 2. |
| */ |
| @Test |
| public void testNestedFunction() throws Exception { |
| check( |
| "func1(field1, func2(field2))", |
| "{fn func1(field1, {fn func2(field2)})}" |
| ); |
| |
| check( |
| "select func1(field1, func2(field2))", |
| "select {fn func1(field1, {fn func2(field2)})}" |
| ); |
| |
| check( |
| "select func1(field1, func2(field2), field3) from SomeTable;", |
| "select {fn func1(field1, {fn func2(field2)}, field3)} from SomeTable;" |
| ); |
| } |
| |
| /** |
| * Test nested escape sequences. Depth > 2. |
| */ |
| @Test |
| public void testDeepNestedFunction() { |
| check( |
| "func1(func2(func3(field1)))", |
| "{fn func1({fn func2({fn func3(field1)})})}" |
| ); |
| |
| check( |
| "func1(func2(func3(func4(field1))))", |
| "{fn func1({fn func2({fn func3({fn func4(field1)})})})}" |
| ); |
| |
| check( |
| "select func1(field1, func2(func3(field2), field3))", |
| "select {fn func1(field1, {fn func2({fn func3(field2)}, field3)})}" |
| ); |
| |
| check( |
| "select func1(field1, func2(func3(field2), field3)) from SomeTable;", |
| "select {fn func1(field1, {fn func2({fn func3(field2)}, field3)})} from SomeTable;" |
| ); |
| } |
| |
| /** |
| * Test series of nested escape sequences. |
| */ |
| @Test |
| public void testNestedFunctionMixed() { |
| check( |
| "func1(func2(field1), func3(field2))", |
| "{fn func1({fn func2(field1)}, {fn func3(field2)})}" |
| ); |
| |
| check( |
| "select func1(func2(field1), func3(field2)) from table;", |
| "select {fn func1({fn func2(field1)}, {fn func3(field2)})} from table;" |
| ); |
| |
| check( |
| "func1(func2(func3(field1))) func1(func2(field2))", |
| "{fn func1({fn func2({fn func3(field1)})})} {fn func1({fn func2(field2)})}" |
| ); |
| } |
| |
| /** |
| * Test invalid escape sequence. |
| */ |
| @Test |
| public void testFailedOnInvalidFunctionSequence() { |
| checkFail("{fnfunc1()}"); |
| |
| checkFail("select {fn func1(field1, {fn func2(field2), field3)} from SomeTable;"); |
| |
| checkFail("select {fn func1(field1, fn func2(field2)}, field3)} from SomeTable;"); |
| } |
| |
| /** |
| * Test escape sequences with additional whitespace characters |
| */ |
| @Test |
| public void testFunctionEscapeSequenceWithWhitespaces() throws Exception { |
| check("func1()", "{ fn func1()}"); |
| |
| check("func1()", "{ fn func1()}"); |
| |
| check("func1()", "{ \n fn\nfunc1()}"); |
| |
| checkFail("{ \n func1()}"); |
| } |
| |
| /** |
| * Test guid escape sequences |
| */ |
| @Test |
| public void testGuidEscapeSequence() { |
| check( |
| "CAST('12345678-9abc-def0-1234-123456789abc' AS UUID)", |
| "{guid '12345678-9abc-def0-1234-123456789abc'}" |
| ); |
| |
| check( |
| "select CAST('12345678-9abc-def0-1234-123456789abc' AS UUID) from SomeTable;", |
| "select {guid '12345678-9abc-def0-1234-123456789abc'} from SomeTable;" |
| ); |
| |
| check( |
| "select CAST('12345678-9abc-def0-1234-123456789abc' AS UUID)", |
| "select {guid '12345678-9abc-def0-1234-123456789abc'}" |
| ); |
| } |
| |
| /** |
| * Test invalid escape sequence. |
| */ |
| @Test |
| public void testFailedOnInvalidGuidSequence() { |
| checkFail("select {guid'12345678-9abc-def0-1234-123456789abc'}"); |
| |
| checkFail("select {guid 12345678-9abc-def0-1234-123456789abc'}"); |
| |
| checkFail("select {guid '12345678-9abc-def0-1234-123456789abc}"); |
| |
| checkFail("select {guid '12345678-9abc-def0-1234-123456789abc' from SomeTable;"); |
| |
| checkFail("select guid '12345678-9abc-def0-1234-123456789abc'} from SomeTable;"); |
| |
| checkFail("select {guid '1234567-1234-1234-1234-123456789abc'}"); |
| |
| checkFail("select {guid '1234567-8123-4123-4123-4123456789abc'}"); |
| |
| checkFail("select {guid '12345678-9abc-defg-1234-123456789abc'}"); |
| |
| checkFail("select {guid '12345678-12345678-1234-1234-1234-123456789abc'}"); |
| |
| checkFail("select {guid '12345678-1234-1234-1234-123456789abcdef'}"); |
| } |
| |
| /** |
| * Test escape sequences with additional whitespace characters |
| */ |
| @Test |
| public void testGuidEscapeSequenceWithWhitespaces() throws Exception { |
| check( |
| "CAST('12345678-9abc-def0-1234-123456789abc' AS UUID)", |
| "{ guid '12345678-9abc-def0-1234-123456789abc'}" |
| ); |
| |
| check( |
| "CAST('12345678-9abc-def0-1234-123456789abc' AS UUID)", |
| "{ guid '12345678-9abc-def0-1234-123456789abc'}" |
| ); |
| |
| check( |
| "CAST('12345678-9abc-def0-1234-123456789abc' AS UUID)", |
| "{ \n guid\n'12345678-9abc-def0-1234-123456789abc'}" |
| ); |
| } |
| |
| /** |
| * Test date escape sequences |
| */ |
| @Test |
| public void testDateEscapeSequence() throws Exception { |
| check( |
| "'2016-08-26'", |
| "{d '2016-08-26'}" |
| ); |
| |
| check( |
| "select '2016-08-26'", |
| "select {d '2016-08-26'}" |
| ); |
| |
| check( |
| "select '2016-08-26' from table;", |
| "select {d '2016-08-26'} from table;" |
| ); |
| } |
| |
| /** |
| * Test date escape sequences with additional whitespace characters |
| */ |
| @Test |
| public void testDateEscapeSequenceWithWhitespaces() throws Exception { |
| check("'2016-08-26'", "{ d '2016-08-26'}"); |
| |
| check("'2016-08-26'", "{ d '2016-08-26'}"); |
| |
| check("'2016-08-26'", "{ \n d\n'2016-08-26'}"); |
| } |
| |
| /** |
| * Test invalid escape sequence. |
| */ |
| @Test |
| public void testFailedOnInvalidDateSequence() { |
| checkFail("{d'2016-08-26'}"); |
| |
| checkFail("{d 2016-08-26'}"); |
| |
| checkFail("{d '2016-08-26}"); |
| |
| checkFail("{d '16-08-26'}"); |
| |
| checkFail("{d '2016/08/02'}"); |
| |
| checkFail("select {d '2016-08-26' from table;"); |
| |
| checkFail("select {}d '2016-08-26'} from table;"); |
| } |
| |
| /** |
| * Test date escape sequences |
| */ |
| @Test |
| public void testTimeEscapeSequence() throws Exception { |
| check("'13:15:08'", "{t '13:15:08'}"); |
| |
| check("select '13:15:08'", "select {t '13:15:08'}"); |
| |
| check("select '13:15:08' from table;", "select {t '13:15:08'} from table;" |
| ); |
| } |
| |
| /** |
| * Test date escape sequences with additional whitespace characters |
| */ |
| @Test |
| public void testTimeEscapeSequenceWithWhitespaces() throws Exception { |
| check("'13:15:08'", "{ t '13:15:08'}"); |
| |
| check("'13:15:08'", "{ t '13:15:08'}"); |
| |
| check("'13:15:08'", "{ \n t\n'13:15:08'}"); |
| } |
| |
| /** |
| * Test invalid escape sequence. |
| */ |
| @Test |
| public void testFailedOnInvalidTimeSequence() { |
| checkFail("{t'13:15:08'}"); |
| |
| checkFail("{t 13:15:08'}"); |
| |
| checkFail("{t '13:15:08}"); |
| |
| checkFail("{t '13 15:08'}"); |
| |
| checkFail("{t '3:15:08'}"); |
| |
| checkFail("select {t '13:15:08' from table;"); |
| |
| checkFail("select {}t '13:15:08'} from table;"); |
| } |
| |
| /** |
| * Test timestamp escape sequences |
| */ |
| @Test |
| public void testTimestampEscapeSequence() throws Exception { |
| check( |
| "'2016-08-26 13:15:08'", |
| "{ts '2016-08-26 13:15:08'}" |
| ); |
| |
| check( |
| "'2016-08-26 13:15:08.123456'", |
| "{ts '2016-08-26 13:15:08.123456'}" |
| ); |
| |
| check( |
| "select '2016-08-26 13:15:08'", |
| "select {ts '2016-08-26 13:15:08'}" |
| ); |
| |
| check( |
| "select '2016-08-26 13:15:08' from table;", |
| "select {ts '2016-08-26 13:15:08'} from table;" |
| ); |
| } |
| |
| /** |
| * Test timestamp escape sequences with additional whitespace characters |
| */ |
| @Test |
| public void testTimestampEscapeSequenceWithWhitespaces() throws Exception { |
| check("'2016-08-26 13:15:08'", |
| "{ ts '2016-08-26 13:15:08'}" |
| ); |
| |
| check("'2016-08-26 13:15:08'", |
| "{ ts '2016-08-26 13:15:08'}" |
| ); |
| |
| check("'2016-08-26 13:15:08'", |
| "{ \n ts\n'2016-08-26 13:15:08'}" |
| ); |
| } |
| |
| /** |
| * Test invalid escape sequence. |
| */ |
| @Test |
| public void testFailedOnInvalidTimestampSequence() { |
| checkFail("{ts '2016-08-26 13:15:08,12345'}"); |
| |
| checkFail("{ts'2016-08-26 13:15:08'}"); |
| |
| checkFail("{ts 2016-08-26 13:15:08'}"); |
| |
| checkFail("{ts '2016-08-26 13:15:08}"); |
| |
| checkFail("{ts '16-08-26 13:15:08'}"); |
| |
| checkFail("{ts '2016-08-26 3:25:08'}"); |
| |
| checkFail("{ts '2016-08 26 03:25:08'}"); |
| |
| checkFail("{ts '2016-08-26 03 25:08'}"); |
| |
| checkFail("{t s '2016-08-26 13:15:08''}"); |
| |
| checkFail("select {ts '2016-08-26 13:15:08' from table;"); |
| |
| checkFail("select {}ts '2016-08-26 13:15:08'} from table;"); |
| } |
| |
| |
| /** |
| * Test escape sequence series. |
| */ |
| @Test |
| public void testOuterJoinFunction() throws Exception { |
| check( |
| "t OUTER JOIN t2 ON t.id=t2.id", |
| "{oj t OUTER JOIN t2 ON t.id=t2.id}" |
| ); |
| |
| check( |
| "select * from t OUTER JOIN t2 ON t.id=t2.id", |
| "select * from {oj t OUTER JOIN t2 ON t.id=t2.id}" |
| ); |
| |
| check( |
| "select * from t OUTER JOIN t2 ON t.id=t2.id ORDER BY t2.id", |
| "select * from {oj t OUTER JOIN t2 ON t.id=t2.id} ORDER BY t2.id" |
| ); |
| } |
| |
| /** |
| * Test simple nested escape sequences. Depth = 2. |
| */ |
| @Test |
| public void testNestedOuterJoin() throws Exception { |
| check( |
| "t OUTER JOIN (t2 OUTER JOIN t3 ON t2.id=t3.id) ON t.id=t2.id", |
| "{oj t OUTER JOIN ({oj t2 OUTER JOIN t3 ON t2.id=t3.id}) ON t.id=t2.id}" |
| ); |
| |
| check( |
| "select * from t OUTER JOIN (t2 OUTER JOIN t3 ON t2.id=t3.id) ON t.id=t2.id", |
| "select * from {oj t OUTER JOIN ({oj t2 OUTER JOIN t3 ON t2.id=t3.id}) ON t.id=t2.id}" |
| ); |
| |
| check( |
| "select * from t OUTER JOIN (t2 OUTER JOIN t3 ON t2.id=t3.id) ON t.id=t2.id ORDER BY t2.id", |
| "select * from {oj t OUTER JOIN ({oj t2 OUTER JOIN t3 ON t2.id=t3.id}) ON t.id=t2.id} ORDER BY t2.id" |
| ); |
| } |
| |
| /** |
| * Test nested escape sequences. Depth > 2. |
| */ |
| @Test |
| public void testDeepNestedOuterJoin() { |
| check( |
| "t OUTER JOIN (t2 OUTER JOIN (t3 OUTER JOIN t4 ON t3.id=t4.id) ON t2.id=t3.id) ON t.id=t2.id", |
| "{oj t OUTER JOIN ({oj t2 OUTER JOIN ({oj t3 OUTER JOIN t4 ON t3.id=t4.id}) ON t2.id=t3.id}) ON t.id=t2.id}" |
| ); |
| |
| check( |
| "select * from " + |
| "t OUTER JOIN (t2 OUTER JOIN (t3 OUTER JOIN t4 ON t3.id=t4.id) ON t2.id=t3.id) ON t.id=t2.id", |
| "select * from " + |
| "{oj t OUTER JOIN ({oj t2 OUTER JOIN ({oj t3 OUTER JOIN t4 ON t3.id=t4.id}) ON t2.id=t3.id})" + |
| " ON t.id=t2.id}" |
| ); |
| |
| check( |
| "select * from t OUTER JOIN (t2 OUTER JOIN (t3 OUTER JOIN t4 ON t3.id=t4.id) " + |
| "ON t2.id=t3.id) ON t.id=t2.id ORDER BY t4.id", |
| "select * from {oj t OUTER JOIN ({oj t2 OUTER JOIN ({oj t3 OUTER JOIN t4 ON t3.id=t4.id}) " + |
| "ON t2.id=t3.id}) ON t.id=t2.id} ORDER BY t4.id" |
| ); |
| } |
| |
| /** |
| * Test invalid escape sequence. |
| */ |
| @Test |
| public void testFailedOnInvalidOuterJoinSequence() { |
| checkFail("{ojt OUTER JOIN t2 ON t.id=t2.id}"); |
| |
| checkFail("select {oj t OUTER JOIN ({oj t2 OUTER JOIN t3 ON t2.id=t3.id) ON t.id=t2.id} from SomeTable;"); |
| |
| checkFail("select oj t OUTER JOIN t2 ON t.id=t2.id} from SomeTable;"); |
| } |
| |
| /** |
| * Test escape sequences with additional whitespace characters |
| */ |
| @Test |
| public void testOuterJoinSequenceWithWhitespaces() throws Exception { |
| check( |
| "t OUTER JOIN t2 ON t.id=t2.id", |
| "{ oj t OUTER JOIN t2 ON t.id=t2.id}" |
| ); |
| |
| check( |
| "t OUTER JOIN t2 ON t.id=t2.id", |
| "{ oj t OUTER JOIN t2 ON t.id=t2.id}" |
| ); |
| |
| check( |
| "t OUTER JOIN t2 ON t.id=t2.id", |
| " \n { oj\nt OUTER JOIN t2 ON t.id=t2.id}" |
| ); |
| } |
| |
| /** |
| * Test non-escape sequences. |
| */ |
| @Test |
| public void testNonEscapeSequence() throws Exception { |
| check("'{fn test()}'", "'{fn test()}'"); |
| |
| check("select '{fn test()}'", "select '{fn test()}'"); |
| |
| check( |
| "select '{fn test()}' from table;", |
| "select '{fn test()}' from table;" |
| ); |
| |
| check( |
| "select test('arg') from table;", |
| "select {fn test('arg')} from table;" |
| ); |
| |
| check( |
| "select test('{fn func()}') from table;", |
| "select {fn test('{fn func()}')} from table;" |
| ); |
| |
| check( |
| "'{\\'some literal\\'}'", |
| "'{\\'some literal\\'}'" |
| ); |
| |
| check( |
| "select '{\\'some literal\\'}'", |
| "select '{\\'some literal\\'}'" |
| ); |
| |
| check( |
| "select '{\\'some literal\\'}' from table;", |
| "select '{\\'some literal\\'}' from table;" |
| ); |
| |
| check( |
| "select '{' + func() + '}' from table;", |
| "select '{' + {fn func()} + '}' from table;" |
| ); |
| |
| // quoted two single quotes should be interpret as apostrophe |
| check( |
| "select '{''{fn test()}''}' from table;", |
| "select '{''{fn test()}''}' from table;" |
| ); |
| |
| checkFail("'{fn test()}"); |
| |
| checkFail("{fn func('arg)}"); |
| |
| checkFail("{fn func(arg')}"); |
| } |
| |
| |
| /** |
| * Test escape sequence series. |
| */ |
| @Test |
| public void testSimpleCallProc() throws Exception { |
| check( |
| "CALL test()", |
| "{call test()}" |
| ); |
| |
| check( |
| "select CALL test()", |
| "select {call test()}" |
| ); |
| |
| check( |
| "select CALL test() from table;", |
| "select {call test()} from table;" |
| ); |
| |
| check( |
| "CALL func(field1) CALL func(field2)", |
| "{call func(field1)} {call func(field2)}" |
| ); |
| |
| check( |
| "select CALL func(field1), CALL func(field2)", |
| "select {call func(field1)}, {call func(field2)}" |
| ); |
| |
| check( |
| "select CALL func(field1), CALL func(field2) from table;", |
| "select {call func(field1)}, {call func(field2)} from table;" |
| ); |
| } |
| |
| /** |
| * Test simple nested escape sequences. Depth = 2. |
| */ |
| @Test |
| public void testNestedCallProc() throws Exception { |
| check( |
| "CALL func1(field1, CALL func2(field2))", |
| "{call func1(field1, {call func2(field2)})}" |
| ); |
| |
| check( |
| "select CALL func1(field1, CALL func2(field2))", |
| "select {call func1(field1, {call func2(field2)})}" |
| ); |
| |
| check( |
| "select CALL func1(field1, CALL func2(field2), field3) from SomeTable;", |
| "select {call func1(field1, {call func2(field2)}, field3)} from SomeTable;" |
| ); |
| } |
| |
| /** |
| * Test nested escape sequences. Depth > 2. |
| */ |
| @Test |
| public void testDeepNestedCallProc() { |
| check( |
| "CALL func1(CALL func2(CALL func3(field1)))", |
| "{call func1({call func2({call func3(field1)})})}" |
| ); |
| |
| check( |
| "CALL func1(CALL func2(CALL func3(CALL func4(field1))))", |
| "{call func1({call func2({call func3({call func4(field1)})})})}" |
| ); |
| |
| check( |
| "select CALL func1(field1, CALL func2(CALL func3(field2), field3))", |
| "select {call func1(field1, {call func2({call func3(field2)}, field3)})}" |
| ); |
| |
| check( |
| "select CALL func1(field1, CALL func2(CALL func3(field2), field3)) from SomeTable;", |
| "select {call func1(field1, {call func2({call func3(field2)}, field3)})} from SomeTable;" |
| ); |
| } |
| |
| /** |
| * Test series of nested escape sequences. |
| */ |
| @Test |
| public void testNestedCallProcMixed() { |
| check( |
| "CALL func1(CALL func2(field1), CALL func3(field2))", |
| "{call func1({call func2(field1)}, {call func3(field2)})}" |
| ); |
| |
| check( |
| "select CALL func1(CALL func2(field1), CALL func3(field2)) from table;", |
| "select {call func1({call func2(field1)}, {call func3(field2)})} from table;" |
| ); |
| |
| check( |
| "CALL func1(CALL func2(CALL func3(field1))) CALL func1(CALL func2(field2))", |
| "{call func1({call func2({call func3(field1)})})} {call func1({call func2(field2)})}" |
| ); |
| } |
| |
| /** |
| * Test invalid escape sequence. |
| */ |
| @Test |
| public void testFailedOnInvalidCallProcSequence() { |
| checkFail("{callfunc1()}"); |
| |
| checkFail("select {call func1(field1, {call func2(field2), field3)} from SomeTable;"); |
| |
| checkFail("select {call func1(field1, call func2(field2)}, field3)} from SomeTable;"); |
| } |
| |
| /** |
| * Test escape sequences with additional whitespace characters |
| */ |
| @Test |
| public void testCallProcEscapeSequenceWithWhitespaces() throws Exception { |
| check("CALL func1()", "{ call func1()}"); |
| |
| check("CALL func1()", "{ call func1()}"); |
| |
| check("CALL func1()", "{ \n call\nfunc1()}"); |
| |
| checkFail("{ \n func1()}"); |
| } |
| |
| /** |
| * Test escape sequence series. |
| */ |
| @Test |
| public void testLikeEscapeSequence() throws Exception { |
| check( |
| "ESCAPE '\\'", |
| "{'\\'}" |
| ); |
| |
| check( |
| "ESCAPE '\\'", |
| "{escape '\\'}" |
| ); |
| |
| check( |
| "ESCAPE ''", |
| "{''}" |
| ); |
| |
| check( |
| "ESCAPE ''", |
| "{escape ''}" |
| ); |
| |
| check( |
| "select * from t where value LIKE '\\%AAA%' ESCAPE '\\'", |
| "select * from t where value LIKE '\\%AAA%' {'\\'}" |
| ); |
| |
| check( |
| "select * from t where value LIKE '\\%AAA%' ESCAPE '\\'", |
| "select * from t where value LIKE '\\%AAA%' {escape '\\'}" |
| ); |
| |
| check( |
| "select * from t where value LIKE '\\%AAA%' ESCAPE '\\' ORDER BY id;", |
| "select * from t where value LIKE '\\%AAA%' {'\\'} ORDER BY id;" |
| ); |
| |
| check( |
| "select * from t where value LIKE '\\%AAA%' ESCAPE '\\' ORDER BY id;", |
| "select * from t where value LIKE '\\%AAA%' {escape '\\'} ORDER BY id;" |
| ); |
| |
| check( |
| "select * from t where value LIKE '\\%AAA''s%' ESCAPE '\\'", |
| "select * from t where value LIKE '\\%AAA''s%' {escape '\\'}" |
| ); |
| } |
| |
| /** |
| * Test escape sequences with additional whitespace characters |
| */ |
| @Test |
| public void testLikeEscapeSequenceWithWhitespaces() throws Exception { |
| check("ESCAPE '\\'", "{ '\\' }"); |
| check("ESCAPE '\\'", "{ escape '\\'}"); |
| |
| check("ESCAPE '\\'", "{ '\\' }"); |
| check("ESCAPE '\\'", "{ escape '\\' }"); |
| |
| check("ESCAPE '\\'", "{ \n '\\' }"); |
| check("ESCAPE '\\'", "{ \n escape\n'\\' }"); |
| } |
| |
| /** |
| * Test invalid escape sequence. |
| */ |
| @Test |
| public void testLikeOnInvalidLikeEscapeSequence() { |
| checkFail("LIKE 'AAA's'"); |
| checkFail("LIKE 'AAA\'s'"); |
| |
| checkFail("LIKE '\\%AAA%' {escape'\\' }"); |
| |
| checkFail("LIKE '\\%AAA%' {'\\' ORDER BY id"); |
| checkFail("LIKE '\\%AAA%' {escape '\\' ORDER BY id;"); |
| |
| checkFail("LIKE '\\%AAA%' '\\'} ORDER BY id"); |
| checkFail("LIKE '\\%AAA%' escape '\\'} ORDER BY id;"); |
| } |
| |
| /** |
| * Check parsing logic. |
| * |
| * @param exp Expected result. |
| * @param qry SQL query text. |
| */ |
| private void check(String exp, String qry) { |
| String actualRes = OdbcEscapeUtils.parse(qry); |
| |
| assertEquals(exp, actualRes); |
| } |
| |
| /** |
| * Check that query parsing fails. |
| * |
| * @param qry Query. |
| */ |
| private void checkFail(final String qry) { |
| GridTestUtils.assertThrows(null, new Callable<Void>() { |
| @Override public Void call() throws Exception { |
| OdbcEscapeUtils.parse(qry); |
| |
| fail("Parsing should fail: " + qry); |
| |
| return null; |
| } |
| }, IgniteException.class, null); |
| } |
| } |