JDBC and MSSQL Datetimeoffset

Hi all,

Long time reader, first time poster. :slight_smile:

I was looking into some discussions here about the JDBC driver for SQL Server and Ignition and I'm a little confused as to why everything doesn't play nicely together. I had read most of the posts on this topic across the forums but many of them were a few years old.

For those who haven't tried it, there will be an error generated when attempting to read a SQL Server resultset that contains a datetimeoffset type (unknown SQL type : -155).

I've done a bit of trial and error, and from what I can tell on the SQL server side, you can convert the datetimeoffset to datetime2 or varchar or a unix timestamp before selecting it in a resultset and it will read appropriately, but it means any time you pass dates back to Ignition those columns need to be converted.

If you are passing dates from Ignition into stored procedures or functions, you can specify an OffsetDateTime and pass it as a string, and that seems to convert okay.

From what I can tell, the JDBC driver has its own DateTimeOffset class which can be converted to a timestamp or other Java types, and other uses of the JDBC driver use that intermediate class to read those columns. For whatever reason, that doesn't seem to be an option with Ignition (as far as I can tell). There's also no connection settings that seem to modify how the dates get returned (again, as far as I can tell).

The conversion option will work, but it seems very strange when it seems like everything is speaking the same language already.

Has anyone run into a solution for this?

Microsoft, as usual, didn't implement their JDBC driver using standard JDBC data types.

Ignition supports the JDBC standard so that it can talk to many brands of database. For technical reasons (connection pooling in particular), the true JDBC connection is not exposed elsewhere in Ignition, so brand-specific datatypes and methods are not supported. Brand-specific layers in such drivers are used to lock users into a particular brand. Also the usual behavior from Microsoft.

The short term solution is as you've identified--convert your column types in views or procedures.

The long term solution is to fire Microsoft and deploy a competent, standards-compliant database technology. I generally recommend PostgreSQL.

Hi pturmel! I've certainly read a few of your posts on this topic. :slight_smile:

I would not expect anything beyond the JDBC layer to know about anything beyond the standard Java classes. My confusion is that the Microsoft-specific class can be converted to the standard Java classes, and so nothing beyond the parts of ignition that handle JDBC connections need to know anything about Microsoft and their database classes. Like if I can convert to a timestamp or UTC or whatever in SQL, surely the Ignition database methods can handle that conversion instead?

No. Only the JDBC vanilla methods are available on the JDBC layer in Ignition. Nothing in Ignition is "Microsoft-aware" (or Oracle-aware or PosgreSQL-aware), so cannot invoke the conversions if it doesn't automatically happen in the resultsets. And it doesn't, because Microsoft.

You should fire them if at all possible.

Aha, I think I understand. Basically, the JDBC driver for Microsoft doesn't make that conversion available in a standard Java way, and Ignition isn't going to make the extra step to convert a Microsoft-specific class, so there's a logical impasse which I see as an exception.

When I'm reading folks talking about extending Java ORMs to handle datetimeoffset with the Microsoft JDBC driver, they're basically bridging that gap for themselves by handling the conversion. That said, presumably I could do the same thing even if it's not a standard Ignition feature. Hmm...

As I mentioned I read your posts before so I have considered Postgres - it isn't not an option but given the state of the project I'm working on, I don't think I could justify swapping database technologies for convenience in datetime types. Sometimes you've got to make do with what you have. :slight_smile:

I forgot to say thank you for the help earlier, but thank you for the information - you've given me plenty to think about.

No, probably not. But these:

  • Standards compliance (identifier quotes, limit clauses, on conflict clauses, et cetera)
  • Life cycle cost (particularly no need for CALs, nor for $$ extra functionality)
  • Non-windows deployment with full features (MS's SQL Server for Linux is not) so you can avoid Windows in production environments

You should fire them if at all possible.

For sake of argument, I'll say it's not possible, and I'm definitely going to agree to disagree on the topic of what constitutes engineering malpractice :slight_smile:

Heh. Everyone has opinions. They can even be wrong. :man_shrugging: :rofl:

Anyhow, from my tinkering, I can use the gateway database connections to query a datetimeoffset, extract it in the resultset, and do things with it (convert to timestamp, etc). It looks like running it through the conversion from a JDBC resultset to the internal Ignition class(es?) like BasicStreamingDataset is where Ignition gets grumpy with it, which I guess tracks with the above discussion.

I was hoping there might be some way to get the data into an agreeable object or fall back to a string conversion - ie, if there are easy approaches to add to the list of types to map. I did see Ignition's TypeMappings class which doesn't look to be modifiable, but is definitely missing the -155 SQL type.

I imagine there are some brute force options if I really wanted to make it work, but it would be way more effort than using a convert to datetime2 :slight_smile:

Hi all! Sorry for the thread necromancy, wasn't sure if it was best to drop in a new thread or add to the same discussion.

I noticed there's a preview version of the JDBC SQL Server driver 13.5 that includes a change that might be relevant:

Deprecate microsoft.sql.DateTimeOffset in Favor of java.time.OffsetDateTime ยท Issue #2905 ยท microsoft/mssql-jdbc

I'm wondering if that changes the supportability of this from the Ignition side? I dont know enough of the Ignition guts to figure out how to tie all the pieces together - I gave it a whirl and it still looks like the driver is returning the microsoft type by default.

I don't think this changes anything - support on the Ignition side has always hinged on adding an explicit mapping from the MSSQL vendor-specific type to e.g. java.util.Date and then implementing the conversion from microsoft.sql.DateTimeOffset to Date where appropriate.

Thanks for the answer. I admit I'm still very confused. I understand the mapping would have to be added but there's implemented conversions to standard Java types. Eg, in a gateway scope I can do something like:

	from com.inductiveautomation.ignition.common.util import LoggerEx
	from com.inductiveautomation.ignition.gateway import IgnitionGateway
	from java.time import OffsetDateTime

	gateway = IgnitionGateway.get()
	connection = gateway.getDatasourceManager().getDatasource("MSSQL").getConnection()
	
	builder = LoggerEx.newBuilder()
	logger = builder.build('sql.test')
	statement = connection.prepareStatement("select sysdatetimeoffset()")
	rs = statement.executeQuery()
	rs.next()
	logger.info(str(rs.getObject(1, OffsetDateTime)))

	connection.close()

or even

	logger.info(str(rs.getTimestamp(1)))

and I would be presumably able to utilize the data from there. I wouldn't exactly want to implement my own dataset handling for a variety of reasons but...what more would the driver need to do to be supportable from Ignition? Always return an OffsetDateTime?

It doesn't need anything else. Somebody just needs to do the work in Ignition.

Approximately none of Ignition's codebase uses the java.time API, we're basically stuck with java.util.Date, so even if we started handing you OffsetDateTimes automatically interoperability with everything else in Ignition is going to be painful.

Presumably we would just map/convert it to java.util.Date if we added support though, rather than preserving it as OffsetDateTime.

When you get there, consider mapping to java.sql.Timestamp, to retain sub-millisecond precision where present.

Yeah, java.sql.Timestamp is what we're doing with some of the Oracle vendor-specific types.

Yeah, I wasn't sure how well Ignition supported TZ-aware times in general, but however Ignition handles other non MSSQL database TZ-aware times would be suitable. If that's a Timestamp, that's probably fine.

In any case it's at least better than blowing up. :slight_smile:

Is this something I should put in a feature request and hope it might make its way in someday? (maybe it's already in there)

I just created a ticket for this. Feature requests or support tickets are the "official" way to make that happen if you don't want to rely on the whims of a random employee seeing your forum post and making a ticket from it.

Perfectly, in the case of PostgreSQL.