Skip to content

Commit 83f9066

Browse files
feat: parse Oracle RAW(n) and NUMBER(*, s) data types (#2875)
DDL exported from Oracle with DBMS_METADATA.GET_DDL prints an INTEGER column as NUMBER(*,0) and binary columns as RAW(16). CREATE TABLE and ALTER TABLE with such columns failed to parse, and so did CAST(x AS NUMBER(*,0)) and CAST(x AS RAW(16)). RAW is a keyword token, so ColDataType did not accept it as a type name. NUMBER(*, s) now takes the same path as NUMBER(p, s): DataType() accepts * in its precision slot for NUMBER only, and ColDataType folds it into the type name as "NUMBER (*, 0)" with a null precision and the scale set. NUMBER(*, *) and VARCHAR(*) stay rejected.
1 parent 4b983b2 commit 83f9066

3 files changed

Lines changed: 40 additions & 6 deletions

File tree

‎src/main/java/net/sf/jsqlparser/statement/create/table/ColDataType.java‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,10 @@ public static ColDataType fromTypeParameters(String dataType, String precision,
103103
private void setNumericTypeParameters(String parameter, Integer scale) {
104104
if (parameter != null) {
105105
maxPrecision = "MAX".equalsIgnoreCase(parameter);
106-
precision = maxPrecision ? null : new BigInteger(parameter);
106+
// Oracle's NUMBER(*, s) has no precision of its own.
107+
precision = maxPrecision || "*".equals(parameter) ? null : new BigInteger(parameter);
107108
this.scale = scale;
108-
dataType += " (" + (maxPrecision ? "MAX" : precision)
109+
dataType += " (" + (maxPrecision ? "MAX" : precision != null ? precision : parameter)
109110
+ (scale != null ? ", " + scale : "") + ")";
110111
}
111112
}
@@ -330,9 +331,10 @@ public ColDataType withXmlTypeModifier(XmlTypeModifier xmlTypeModifier) {
330331
* The first numeric type parameter, e.g. {@code 255} for {@code VARCHAR(255)} or {@code 10} for
331332
* {@code DECIMAL(10, 2)}. {@code MAX} is reported as {@link Integer#MAX_VALUE}. Returns
332333
* {@code null} when the type carries no numeric parameters, e.g. {@code INT} or
333-
* {@code ENUM('a', 'b')}, or when a numeric length exceeds the integer range. Use
334-
* {@link #getNumericPrecision()} for the full range and {@link #isMaxPrecision()} to
335-
* distinguish MAX from the numeric value 2147483647.
334+
* {@code ENUM('a', 'b')}, when the precision is Oracle's {@code *} as in {@code NUMBER(*, 0)},
335+
* or when a numeric length exceeds the integer range. Use {@link #getNumericPrecision()} for
336+
* the full range and {@link #isMaxPrecision()} to distinguish MAX from the numeric value
337+
* 2147483647.
336338
*/
337339
public Integer getPrecision() {
338340
return maxPrecision ? Integer.valueOf(Integer.MAX_VALUE)

‎src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18924,7 +18924,10 @@ ColDataType DataType():
1892418924
| tk=<K_CHAR> | tk=<K_JSON> | tk=<K_STRING> ) { type += " " + tk.image; }
1892518925
)*
1892618926
[
18927-
LOOKAHEAD(2) "(" ( tk=<S_LONG> { precision = tk.image; } | tk=<K_MAX> { precision = "MAX"; } )
18927+
LOOKAHEAD(2) "(" ( tk=<S_LONG> { precision = tk.image; } | tk=<K_MAX> { precision = "MAX"; }
18928+
// Oracle's NUMBER(*, s) and NUMBER(*)
18929+
| "*" { precision = "*"; requireDdlSyntax("NUMBER".equalsIgnoreCase(type),
18930+
"Wildcard precision is only supported for NUMBER"); } )
1892818931
[ <K_BYTE> | <K_CHAR> ]
1892918932
[ "," scaleText=SignedIntegerTypeArgument() { scale = Integer.valueOf(scaleText); } ]
1893018933
")"
@@ -19067,6 +19070,8 @@ ColDataType ColDataType():
1906719070
| tk=<K_NAME>
1906819071
// PostgreSQL's geometric type
1906919072
| tk=<K_PATH>
19073+
// Oracle's RAW(n)
19074+
| tk=<K_RAW>
1907019075
)
1907119076
{
1907219077
schema = tk.image;

‎src/test/java/net/sf/jsqlparser/statement/create/table/ColDataTypeTest.java‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111

1212
import net.sf.jsqlparser.JSQLParserException;
1313
import net.sf.jsqlparser.expression.IntervalQualifier;
14+
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
1415
import net.sf.jsqlparser.test.TestUtils;
1516
import org.junit.jupiter.api.Test;
1617

1718
import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed;
1819
import static org.junit.jupiter.api.Assertions.assertEquals;
1920
import static org.junit.jupiter.api.Assertions.assertNotNull;
2021
import static org.junit.jupiter.api.Assertions.assertNull;
22+
import static org.junit.jupiter.api.Assertions.assertThrows;
2123

2224
class ColDataTypeTest {
2325
@Test
@@ -147,6 +149,31 @@ void testStructuredPrecisionForZonedTypes() throws JSQLParserException {
147149
assertNull(unparameterized.getPrecision());
148150
}
149151

152+
@Test
153+
void testOracleNumberWithWildcardPrecision() throws JSQLParserException {
154+
ColDataType number =
155+
parseColumnType("CREATE TABLE t (a NUMBER(*, 0), b NUMBER(*), c NUMBER(*, 2))");
156+
assertEquals("NUMBER (*, 0)", number.getDataType());
157+
assertEquals("NUMBER", number.getBaseTypeName());
158+
assertNull(number.getPrecision());
159+
assertEquals(0, number.getScale());
160+
161+
assertSqlCanBeParsedAndDeparsed("SELECT CAST(1.5 AS NUMBER(*, 0)) FROM dual", true);
162+
163+
assertThrows(JSQLParserException.class,
164+
() -> CCJSqlParserUtil.parseColDataType("NUMBER(*, *)"));
165+
}
166+
167+
@Test
168+
void testOracleRawType() throws JSQLParserException {
169+
ColDataType raw = parseColumnType("CREATE TABLE t (id RAW(16), payload raw(2000))");
170+
assertEquals("RAW", raw.getDataType());
171+
assertEquals(16, raw.getPrecision());
172+
173+
assertSqlCanBeParsedAndDeparsed("ALTER TABLE t ADD payload RAW(2000)", true);
174+
assertSqlCanBeParsedAndDeparsed("SELECT CAST(HEXTORAW('AB') AS RAW(16)) FROM dual", true);
175+
}
176+
150177
private ColDataType parseColumnType(String sqlStr) throws JSQLParserException {
151178
CreateTable create = (CreateTable) assertSqlCanBeParsedAndDeparsed(sqlStr, true);
152179
return create.getColumnDefinitions().get(0).getColDataType();

0 commit comments

Comments
 (0)