Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

java.lang.UnsupportedOperationException: Can not anonymize objects of type class java.sql.Timestamp #144

Open
aminismaila opened this issue Oct 2, 2021 · 5 comments

Comments

@aminismaila
Copy link

aminismaila commented Oct 2, 2021

Anonimatron version: 1.15
Operating system and version: Windows 10 Pro 20H2
Java runtime (java -version):
1.8.0_291

Executed commands or actions:

anonimatron.bat -config <config_file> -synonyms <synonym_file>

Expected outcome or behavior:

Date field should get anonymized. Field is in an oracle database with datatype DATE

Actual outcome or behavior:

java.lang.UnsupportedOperationException: Can not anonymize objects of type class java.sql.Timestamp

@realrolfje
Copy link
Owner

I see that a DateTime anonymizer is indeed missing. There is a java.sql.Date anonymizer, that may be a quickfix while the TimeStampAnonymizer is built. Can you try to do this in your configuration and see what it does:

        <column name="A_DATE_COLUMN" type="DATE" size="-1"/>

Otherwise I'll have to build a TimeStampAnonymizer for you (I think that is a good addition anyway)

@aminismaila
Copy link
Author

Below is the table definition:

SQL> desc hr.employees;
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 EMPLOYEE_ID                               NOT NULL NUMBER(6)
 FIRST_NAME                                         VARCHAR2(20)
 LAST_NAME                                 NOT NULL VARCHAR2(25)
 EMAIL                                     NOT NULL VARCHAR2(25)
 PHONE_NUMBER                                       VARCHAR2(20)
 HIRE_DATE                                 NOT NULL DATE
 JOB_ID                                    NOT NULL VARCHAR2(10)
 SALARY                                             NUMBER(8,2)
 COMMISSION_PCT                                     NUMBER(2,2)
 MANAGER_ID                                         NUMBER(6)
 DEPARTMENT_ID                                      NUMBER(4)

And below is my config file:


<?xml version="1.0" encoding="UTF-8"?>
<configuration jdbcurl="jdbc:oracle:thin:@localhost:1521:choggu" userid="hr" password="hr">
       <table name="EMPLOYEES">
        <column name="HIRE_DATE" type="DATE" size="-1"/>
	   </table>
</configuration>

But I still get the error. Besides, since the column datatype is DATE, why is the error complaining of a TIMESTAMP?

Detailed Error is shown below:


2021-10-03 12:56:17,132 FATAL (JdbcAnonymizerService.java:0) Anonymyzation stopped because of fatal error. [Table 'EMPLOYEES' Column 'HIRE_DATE' Type 'DATE']
java.lang.UnsupportedOperationException: Can not anonymize objects of type class java.sql.Timestamp
        at com.rolfje.anonimatron.anonymizer.DateAnonymizer.anonymize(DateAnonymizer.java:42)
        at com.rolfje.anonimatron.anonymizer.Anonymizer.anonymize(Anonymizer.java:57)
        at com.rolfje.anonimatron.anonymizer.AnonymizerService.anonymize(AnonymizerService.java:92)
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.lambda$anonymizeTableInPlace$2(JdbcAnonymizerService.java:115)
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.processTableColumns(JdbcAnonymizerService.java:192)
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.anonymizeTableInPlace(JdbcAnonymizerService.java:128)
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.anonymize(JdbcAnonymizerService.java:81)
        at com.rolfje.anonimatron.Anonimatron.anonymize(Anonimatron.java:94)
        at com.rolfje.anonimatron.Anonimatron.main(Anonimatron.java:48)
Anonymizing table 'EMPLOYEES', total progress  [0%, ETA 12:58:04 PM]Exception in thread "main" java.lang.RuntimeException: java.lang.UnsupportedOperationException: Can not anonymize objects of type class java.sql.Timestamp
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.processTableColumns(JdbcAnonymizerService.java:221)
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.anonymizeTableInPlace(JdbcAnonymizerService.java:128)
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.anonymize(JdbcAnonymizerService.java:81)
        at com.rolfje.anonimatron.Anonimatron.anonymize(Anonimatron.java:94)
        at com.rolfje.anonimatron.Anonimatron.main(Anonimatron.java:48)
Caused by: java.lang.UnsupportedOperationException: Can not anonymize objects of type class java.sql.Timestamp
        at com.rolfje.anonimatron.anonymizer.DateAnonymizer.anonymize(DateAnonymizer.java:42)
        at com.rolfje.anonimatron.anonymizer.Anonymizer.anonymize(Anonymizer.java:57)
        at com.rolfje.anonimatron.anonymizer.AnonymizerService.anonymize(AnonymizerService.java:92)
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.lambda$anonymizeTableInPlace$2(JdbcAnonymizerService.java:115)
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.processTableColumns(JdbcAnonymizerService.java:192)
        ... 4 more

Thanks for your help.

@realrolfje
Copy link
Owner

I think I found a possible explanation for this behavior on https://www.oracle.com/database/technologies/faq-jdbc.html (excuse the long copy paste):

Prior to 9.2, the Oracle JDBC drivers mapped the DATE SQL type to java.sql.Timestamp. This made a certain amount of sense because the Oracle DATE SQL type contains both date and time information as does java.sql.Timestamp. The more obvious mapping to java.sql.Date was somewhat problematic as java.sql.Date does not include time information. It was also the case that the RDBMS did not support the TIMESTAMP SQL type, so there was no problem with mapping DATE to Timestamp.

In 9.2 TIMESTAMP support was added to the RDBMS. The difference between DATE and TIMESTAMP is that TIMESTAMP includes nanoseconds and DATE does not. So, beginning in 9.2, DATE is mapped to Date and TIMESTAMP is mapped to Timestamp. Unfortunately if you were relying on DATE values to contain time information, there is a problem.

There are several ways to address this problem in the 9.2 through 10.2 drivers:

Alter your tables to use TIMESTAMP instead of DATE. This is probably rarely possible, but it is the best solution when it is.
Alter your application to use defineColumnType to define the columns as TIMESTAMP rather than DATE. There are problems with this because you really don't want to use defineColumnType unless you have to (see What is defineColumnType and when should I use it?).
Alter you application to use getTimestamp rather than getObject. This is a good solution when possible, however many applications contain generic code that relies on getObject, so it isn't always possible.
Set the V8Compatible connection property. This tells the JDBC drivers to use the old mapping rather than the new one. You can set this flag either as a connection property or a system property. You set the connection property by adding it to the java.util.Properties object passed to DriverManager.getConnection or to OracleDataSource.setConnectionProperties. You set the system property by including a -D option in your java command line.
java -Doracle.jdbc.V8Compatible="true" MyApp

Oracle JDBC 11.1 fixes this problem. Beginning with this release the driver maps SQL DATE columns to java.sql.Timestamp by default. There is no need to set V8Compatible to get the correct mapping. V8Compatible is strongly deprecated. You should not use it at all. If you do set it to true it won't hurt anything, but you should stop using it.

Although it was rarely used that way, V8Compatible existed not to fix the DATE to Date issue but to support compatibility with 8i databases. 8i (and older) databases did not support the TIMESTAMP type. Setting V8Compatible not only caused SQL DATE to be mapped to Timestamp when read from the database, it also caused all Timestamps to be converted to SQL DATE when written to the database. Since 8i is desupported, the 11.1 JDBC drivers do not support this compatibility mode. For this reason V8Compatible is desupported.

As mentioned above, the 11.1 drivers by default convert SQL DATE to Timestamp when reading from the database. This always was the right thing to do and the change in 9i was a mistake. The 11.1 drivers have reverted to the correct behavior. Even if you didn't set V8Compatible in your application you shouldn't see any difference in behavior in most cases. You may notice a difference if you use getObject to read a DATE column. The result will be a Timestamp rather than a Date. Since Timestamp is a subclass of Date this generally isn't a problem. Where you might notice a difference is if you relied on the conversion from DATE to Date to truncate the time component or if you do toString on the value. Otherwise the change should be transparent.

If for some reason your app is very sensitive to this change and you simply must have the 9i-10g behavior, there is a connection property you can set. Set mapDateToTimestamp to false and the driver will revert to the default 9i-10g behavior and map DATE to Date.

java.sql.TimeStamp extenda java.util.Date (note the cross-package extend). In Anonimatron I register the Date anonymizer as:

defaultTypeMapping.put(Date.class.getName(), new DateAnonymizer().getType());

But there is no mapping for TimeStamp. Adding the same mapping for TimeStamp should work according to the text above, and would then be transparent. I could also create a specific TimeStamp Anonymizer, this is functionally slightly different and might result in a Date and a TimeStamp column to not be anonymized the same way.

Meanwhile, what you could try according to the Oracle docs is use a different driver version or connection parameters for now.

@aminismaila
Copy link
Author

Many thanks for your assistance, but how do I use a different driver version? How do I know the version currently bundled with anonimatron?

@aminismaila
Copy link
Author

Sorry to bother you again, but I got below error whiles doing a dry run on a table with 1.3 million rows. After about 5 hours I got the error below:

java.lang.UnsupportedOperationException: Could not generate a Roman name which was not generated before and fits within the given column size of 105

Is there a config change I can make to resolve this?

FULL ERROR

2021-10-03 16:02:56,366 INFO  (JdbcAnonymizerService.java:0) Table CUSTOMER has 1366806 rows to process. []
2021-10-03 16:02:56,370 DEBUG (ProgressPrinter.java:0) Progress timer was not started by caller, starting it now to get sensible ETA figures. []
Pre-scanning table 'CUSTOMER', total progress  [0%, ETA 4:02:56 PM]2021-10-03 16:02:57,266 DEBUG (JdbcAnonymizerService.java:0) select CUSTOMER_NO, CUSTOMER_NAME1 from CUSTOMER [Table 'CUSTOMER']                                                                        Pre-scanning table 'CUSTOMER', total progress  [0%, ETA 11:43:03 AM]2021-10-03 16:02:57,870 DEBUG (JdbcAnonymizerService.java:0) The Anonimizer service has indicated that it can skip the rest of table CUSTOMER in this pass. [Table 'CUSTOMER']                                                                                           Anonymizing table 'CUSTOMER', total progress  [0%, ETA 12:55:52 PM]2021-10-03 16:02:59,218 DEBUG (JdbcAnonymizerService.java:0) select CUSTOMER_NO, CUSTOMER_NAME1 from Anonymizing table 'CUSTOMER', total progress  [92%, ETA 9:44:54 PM]2021-10-03 21:19:02,343 FATAL (JdbcAnonymizerService.java:0) Anonymyzation stopped because of fatal error. [Table 'CUSTOMER' Column 'CUSTOMER_NAME1' Type 'ROMAN_NAME']
java.lang.UnsupportedOperationException: Could not generate a Roman name which was not generated before and fits within the given column size of 105.
        at com.rolfje.anonimatron.anonymizer.AbstractNameGenerator.getName(AbstractNameGenerator.java:417)
        at com.rolfje.anonimatron.anonymizer.AbstractNameGenerator.anonymize(AbstractNameGenerator.java:402)
        at com.rolfje.anonimatron.anonymizer.Anonymizer.anonymize(Anonymizer.java:57)
        at com.rolfje.anonimatron.anonymizer.AnonymizerService.anonymize(AnonymizerService.java:92)
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.lambda$anonymizeTableInPlace$2(JdbcAnonymizerService.java:115)
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.processTableColumns(JdbcAnonymizerService.java:192)
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.anonymizeTableInPlace(JdbcAnonymizerService.java:128)
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.anonymize(JdbcAnonymizerService.java:81)
        at com.rolfje.anonimatron.Anonimatron.anonymize(Anonimatron.java:94)
        at com.rolfje.anonimatron.Anonimatron.main(Anonimatron.java:48)                             Anonymizing table 'CUSTOMER', total progress  [92%, ETA 9:44:55 PM]Exception in thread "main" java.lang.RuntimeException: java.lang.UnsupportedOperationException: Could not generate a Roman name which was not generated before and fits within the given column size of 105.
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.processTableColumns(JdbcAnonymizerService.java:221)
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.anonymizeTableInPlace(JdbcAnonymizerService.java:128)
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.anonymize(JdbcAnonymizerService.java:81)
        at com.rolfje.anonimatron.Anonimatron.anonymize(Anonimatron.java:94)
        at com.rolfje.anonimatron.Anonimatron.main(Anonimatron.java:48)
Caused by: java.lang.UnsupportedOperationException: Could not generate a Roman name which was not generated before and fits within the given column size of 105.
        at com.rolfje.anonimatron.anonymizer.AbstractNameGenerator.getName(AbstractNameGenerator.java:417)
        at com.rolfje.anonimatron.anonymizer.AbstractNameGenerator.anonymize(AbstractNameGenerator.java:402)
        at com.rolfje.anonimatron.anonymizer.Anonymizer.anonymize(Anonymizer.java:57)
        at com.rolfje.anonimatron.anonymizer.AnonymizerService.anonymize(AnonymizerService.java:92)
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.lambda$anonymizeTableInPlace$2(JdbcAnonymizerService.java:115)
        at com.rolfje.anonimatron.jdbc.JdbcAnonymizerService.processTableColumns(JdbcAnonymizerService.java:192)
        ... 4 more

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants