AvaticaSiteFuzzer.java
/*
* 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.calcite.avatica.fuzz;
import org.apache.calcite.avatica.AvaticaParameter;
import org.apache.calcite.avatica.AvaticaSite;
import org.apache.calcite.avatica.remote.TypedValue;
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
/**
* Fuzzer for AvaticaSite.
*/
public class AvaticaSiteFuzzer {
private AvaticaSiteFuzzer() {
}
/**
* Fuzzes AvaticaSite methods.
*
* @param data fuzzed data
*/
public static void fuzzerTestOneInput(FuzzedDataProvider data) {
try {
// Construct dependencies for an AvaticaSite
AvaticaParameter param = new AvaticaParameter(
data.consumeBoolean(),
data.consumeInt(),
data.consumeInt(), // scale
data.consumeInt(),
data.consumeString(10), // typeName
data.consumeString(10), // className
data.consumeString(10) // name
);
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.ROOT);
TypedValue[] slots = new TypedValue[1];
// Target object
AvaticaSite site = new AvaticaSite(param, calendar, 0, slots);
// Determine what to fuzz
int choice = data.consumeInt(1, 16);
switch (choice) {
case 1:
site.setByte(data.consumeByte());
break;
case 2:
site.setChar(data.consumeChar());
break;
case 3:
site.setShort(data.consumeShort());
break;
case 4:
site.setInt(data.consumeInt());
break;
case 5:
site.setLong(data.consumeLong());
break;
case 6:
site.setBoolean(data.consumeBoolean());
break;
case 7:
site.setNString(data.consumeString(50));
break;
case 8:
site.setFloat(data.consumeFloat());
break;
case 9:
site.setDouble(data.consumeDouble());
break;
case 10:
site.setBigDecimal(new BigDecimal(data.consumeDouble()));
break;
case 11:
site.setString(data.consumeString(50));
break;
case 12:
site.setBytes(data.consumeBytes(50));
break;
case 13:
site.setTimestamp(new Timestamp(data.consumeLong()), calendar);
break;
case 14:
site.setTime(new Time(data.consumeLong()), calendar);
break;
case 15:
// Raw object mapping
Object obj = null;
int objType = data.consumeInt(1, 4);
if (objType == 1) {
obj = data.consumeBoolean();
} else if (objType == 2) {
obj = data.consumeString(50);
} else if (objType == 3) {
obj = data.consumeLong();
} else if (objType == 4) {
obj = data.consumeBytes(50);
}
site.setObject(obj, data.consumeInt(-10, 100)); // Types constants fall in this range
break;
case 16:
// Test the JDBC ResultSet getter mapping using a dynamic proxy
org.apache.calcite.avatica.util.Cursor.Accessor accessor =
(org.apache.calcite.avatica.util.Cursor.Accessor) Proxy.newProxyInstance(
org.apache.calcite.avatica.util.Cursor.Accessor.class.getClassLoader(),
new Class<?>[] {org.apache.calcite.avatica.util.Cursor.Accessor.class},
(proxy, method, args) -> {
String name = method.getName();
if (name.equals("wasNull")) {
return data.consumeBoolean();
}
if (name.equals("getString") || name.equals("getNString")) {
return data.consumeString(50);
}
if (name.equals("getBoolean")) {
return data.consumeBoolean();
}
if (name.equals("getByte")) {
return data.consumeByte();
}
if (name.equals("getShort")) {
return data.consumeShort();
}
if (name.equals("getInt")) {
return data.consumeInt();
}
if (name.equals("getLong")) {
return data.consumeLong();
}
if (name.equals("getFloat")) {
return data.consumeFloat();
}
if (name.equals("getDouble")) {
return data.consumeDouble();
}
if (name.equals("getBigDecimal")) {
return new BigDecimal(data.consumeDouble());
}
if (name.equals("getBytes")) {
return data.consumeBytes(50);
}
if (name.equals("getDate")) {
return new Date(data.consumeLong());
}
if (name.equals("getTime")) {
return new Time(data.consumeLong());
}
if (name.equals("getTimestamp")) {
return new Timestamp(data.consumeLong());
}
if (name.equals("getUByte")) {
return org.joou.UByte.valueOf(data.consumeInt(0, 255));
}
if (name.equals("getUShort")) {
return org.joou.UShort.valueOf(data.consumeInt(0, 65535));
}
if (name.equals("getUInt")) {
return org.joou.UInteger.valueOf(data.consumeLong(0, 4294967295L));
}
if (name.equals("getULong")) {
return org.joou.ULong.valueOf(data.consumeLong(0, Long.MAX_VALUE));
}
return null;
}
);
try {
AvaticaSite.get(accessor, data.consumeInt(-10, 100), data.consumeBoolean(), calendar);
} catch (SQLException e) {
// Expected to throw SQLException for unsupported conversions
}
break;
default:
break;
}
} catch (IllegalArgumentException | UnsupportedOperationException e) {
// UnsupportedOperationException is explicitly thrown by AvaticaSite.notImplemented()
// and unsupportedCast() when types don't align.
} catch (RuntimeException e) {
// TypedValue bindings often throw RuntimeException directly for "not implemented"
if (!"not implemented".equals(e.getMessage())) {
throw e;
}
}
}
}