Code review comment: That is not correct. The builder does support same names for fields in different nested levels.  Avro in general does not support same record types with different schemas. And it does it rightly so. Therefore a schema like:  ``` {     "type": "record",      "name": "top",      "fields": [  		  {              "name": "top",               "type": {                   "type": "record",                   "name": "nested",                   "fields": [                       {"type": "string", "name": "top"}                   ]              }           }     ]  } ``` is valid and supported. However if we change the name of the `nested` record to `top` it will be invalid: ``` {     "type": "record",      "name": "top",      "fields": [  		  {              "name": "top",               "type": {                   "type": "record",                   "name": "top",                   "fields": [                       {"type": "string", "name": "top"}                   ]              }           }     ]  } ```  I think the core problem lays in how the `rowName` is generated. I think we should never adjust the `fieldName`, but we should append the `fieldName` to the `rowName`.  BTW another shortcoming that I see is that we are losing the record name when converting from `Schema` to `DataType`. I think it is not a real issue though.

Code under review:
public static Schema convertToSchema(LogicalType logicalType, String rowName) {
		int precision;
		switch (logicalType.getTypeRoot()) {
			case NULL:
				return SchemaBuilder.builder().nullType();
			case BOOLEAN:
				return getNullableBuilder(logicalType).booleanType();
			case TINYINT:
			case SMALLINT:
			case INTEGER:
				return getNullableBuilder(logicalType).intType();
			case BIGINT:
				return getNullableBuilder(logicalType).longType();
			case FLOAT:
				return getNullableBuilder(logicalType).floatType();
			case DOUBLE:
				return getNullableBuilder(logicalType).doubleType();
			case CHAR:
			case VARCHAR:
				return getNullableBuilder(logicalType).stringType();
			case BINARY:
			case VARBINARY:
				return getNullableBuilder(logicalType).bytesType();
			case TIMESTAMP_WITHOUT_TIME_ZONE:
				
				final TimestampType timestampType = (TimestampType) logicalType;
				precision = timestampType.getPrecision();
				org.apache.avro.LogicalType avroLogicalType;
				if (precision <= 3) {
					avroLogicalType = LogicalTypes.timestampMillis();
				} else {
					throw new IllegalArgumentException("Avro does not support TIMESTAMP type " +
						"with precision: " + precision + ", it only supports precision less than 3.");
				}
				return avroLogicalType.addToSchema(SchemaBuilder.builder().longType());
			case DATE:
				
				return LogicalTypes.date().addToSchema(SchemaBuilder.builder().intType());
			case TIME_WITHOUT_TIME_ZONE:
				precision = ((TimeType) logicalType).getPrecision();
				if (precision > 3) {
					throw new IllegalArgumentException(
						"Avro does not support TIME type with precision: " + precision +
						", it only supports precision less than 3.");
				}
				
				return LogicalTypes.timeMillis().addToSchema(SchemaBuilder.builder().intType());
			case DECIMAL:
				DecimalType decimalType = (DecimalType) logicalType;
				
				return LogicalTypes
					.decimal(decimalType.getPrecision(), decimalType.getScale())
					.addToSchema(SchemaBuilder.builder().bytesType());
			case ROW:
				RowType rowType = (RowType) logicalType;
				List<String> fieldNames = rowType.getFieldNames();
				
				SchemaBuilder.FieldAssembler<Schema> builder = SchemaBuilder
					.builder()
					.record(rowName)
					.fields();
				for (int i = 0; i < rowType.getFieldCount(); i++) {
					String fieldName = fieldNames.get(i);
					if (rowName.equals(fieldName)) {
						
						fieldName = rowName + "_" + fieldName;
					}
					builder = builder
						.name(fieldName)
						.type(convertToSchema(rowType.getTypeAt(i), fieldName))
						.noDefault();
				}
				return builder.endRecord();
			case MULTISET:
			case MAP:
				return SchemaBuilder
					.builder()
					.nullable()
					.map()
					.values(convertToSchema(extractValueTypeToAvroMap(logicalType), rowName));
			case ARRAY:
				ArrayType arrayType = (ArrayType) logicalType;
				return SchemaBuilder
					.builder()
					.nullable()
					.array()
					.items(convertToSchema(arrayType.getElementType(), rowName));
			case RAW:
			case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
			default:
				throw new UnsupportedOperationException("Unsupported to derive Schema for type: " + logicalType);
		}
	}

Line commented on: case MULTISET:

Revised code:
public static Schema convertToSchema(LogicalType logicalType, String rowName) {
		int precision;
		boolean nullable = logicalType.isNullable();
		switch (logicalType.getTypeRoot()) {
			case NULL:
				return SchemaBuilder.builder().nullType();
			case BOOLEAN:
				Schema bool = SchemaBuilder.builder().booleanType();
				return nullable ? nullableSchema(bool) : bool;
			case TINYINT:
			case SMALLINT:
			case INTEGER:
				Schema integer = SchemaBuilder.builder().intType();
				return nullable ? nullableSchema(integer) : integer;
			case BIGINT:
				Schema bigint = SchemaBuilder.builder().longType();
				return nullable ? nullableSchema(bigint) : bigint;
			case FLOAT:
				Schema f = SchemaBuilder.builder().floatType();
				return nullable ? nullableSchema(f) : f;
			case DOUBLE:
				Schema d = SchemaBuilder.builder().doubleType();
				return nullable ? nullableSchema(d) : d;
			case CHAR:
			case VARCHAR:
				Schema str = SchemaBuilder.builder().stringType();
				return nullable ? nullableSchema(str) : str;
			case BINARY:
			case VARBINARY:
				Schema binary = SchemaBuilder.builder().bytesType();
				return nullable ? nullableSchema(binary) : binary;
			case TIMESTAMP_WITHOUT_TIME_ZONE:
				
				final TimestampType timestampType = (TimestampType) logicalType;
				precision = timestampType.getPrecision();
				org.apache.avro.LogicalType avroLogicalType;
				if (precision <= 3) {
					avroLogicalType = LogicalTypes.timestampMillis();
				} else {
					throw new IllegalArgumentException("Avro does not support TIMESTAMP type " +
						"with precision: " + precision + ", it only supports precision less than 3.");
				}
				Schema timestamp = avroLogicalType.addToSchema(SchemaBuilder.builder().longType());
				return nullable ? nullableSchema(timestamp) : timestamp;
			case DATE:
				
				Schema date = LogicalTypes.date().addToSchema(SchemaBuilder.builder().intType());
				return nullable ? nullableSchema(date) : date;
			case TIME_WITHOUT_TIME_ZONE:
				precision = ((TimeType) logicalType).getPrecision();
				if (precision > 3) {
					throw new IllegalArgumentException(
						"Avro does not support TIME type with precision: " + precision +
						", it only supports precision less than 3.");
				}
				
				Schema time = LogicalTypes.timeMillis()
						.addToSchema(SchemaBuilder.builder().intType());
				return nullable ? nullableSchema(time) : time;
			case DECIMAL:
				DecimalType decimalType = (DecimalType) logicalType;
				
				Schema decimal = LogicalTypes
						.decimal(decimalType.getPrecision(), decimalType.getScale())
						.addToSchema(SchemaBuilder.builder().bytesType());
				return nullable ? nullableSchema(decimal) : decimal;
			case ROW:
				RowType rowType = (RowType) logicalType;
				List<String> fieldNames = rowType.getFieldNames();
				
				SchemaBuilder.FieldAssembler<Schema> builder = SchemaBuilder.builder()
						.record(rowName)
						.fields();
				for (int i = 0; i < rowType.getFieldCount(); i++) {
					String fieldName = fieldNames.get(i);
					builder = builder
						.name(fieldName)
						.type(convertToSchema(rowType.getTypeAt(i), rowName + "_" + fieldName))
						.noDefault();
				}
				Schema record = builder.endRecord();
				return nullable ? nullableSchema(record) : record;
			case MULTISET:
			case MAP:
				Schema map = SchemaBuilder.builder()
						.map()
						.values(convertToSchema(extractValueTypeToAvroMap(logicalType), rowName));
				return nullable ? nullableSchema(map) : map;
			case ARRAY:
				ArrayType arrayType = (ArrayType) logicalType;
				Schema array = SchemaBuilder.builder()
						.array()
						.items(convertToSchema(arrayType.getElementType(), rowName));
				return nullable ? nullableSchema(array) : array;
			case RAW:
			case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
			default:
				throw new UnsupportedOperationException("Unsupported to derive Schema for type: " + logicalType);
		}
	}