WIJ Block ComparisonDOC

If you encounter MISMATCH.WIJ, contact InterSystems Worldwide Response Center (WRC) before proceeding.

Note: If a database is to be restored following a crash, ensure that prior to the restore you start the instance without WIJ and journal recovery (see Starting InterSystems IRIS Without Automatic WIJ and Journal Recovery). This avoids both creating discrepancies that will be detected by the WIJ comparison and incorrectly applying WIJ blocks or journal data (see Journaling) to a version of a database for which they were not intended.

Some storage subsystems, particularly local drives on laptops and workstations, use an unsafe form of write-back caching that is not backed by battery or by non-volatile memory. This defeats the two-phase write protocol that InterSystems IRIS performs and can lead to corruption following a hardware crash or power loss that is detected during WIJ compare.

See

Here is a small example:

Class dc.test Extends %Persistent
{

Property pClob As %Stream.GlobalCharacter;

Property pBlob As %Stream.GlobalBinary;

ClassMethod RSToDynamicArray(ByRef rs As %SQL.StatementResultAs %DynamicArray
{
  #dim metaData as %SQL.StatementMetadata rs.%GetMetadata()
  ;d metaData.%Display()
  
  i=1:1:metaData.columns.Count() {
    #dim column As %SQL.StatementColumn metaData.columns.GetAt(i)
    colInfo(i,"label") = column.label,
      colInfo(i,"ODBCType") = $$$GetOdbcTypeName(column.ODBCType)
  }
  colInfo=i
    
  arr []
  while rs.%Next() {
    row {}
    = 1:1:colInfo {
      label=colInfo(i,"label"),
        odbcType=colInfo(i,"ODBCType"),
        val rs.%GetData(i)
        
      val="" {
        row.%Set(label,"","null")
      }elseif odbcType="BIT" {
        row.%Set(label,$num(val),"boolean")
      }elseif odbcType="DATE" {
        row.%Set(label,$zd(val,3))
      }elseif (odbcType="LONGVARCHAR")||(odbcType="LONGVARBINARY"{
        row.%Set(label,##class(%Stream.Object).%Open(val),"stream")
      }elseif $IsValidNum(val{
        row.%Set(label,$num(val),"number")
      }else {
        row.%Set(label,val,"string")
      }
    }
    arr.%Push(row)
  }    
  
  arr
}

ClassMethod Test()
{
  ..%KillExtent()
  
  tmp=..%New()
  tmp.pClob.Write("Hello")
  tmp.pBlob.Write($c(0,1,7))
  tmp.%Save()
  
  sql="select 'hello' fStr" _
  ",2 fInt" _
  ",current_date fDate" _
  ",cast(1 as BIT) fBool" _
  ",null fNull" _
  ",(select top 1 pClob from dc.test) fClob" _
  ",(select top 1 pBlob from dc.test) fBlob"

  #dim rs As %SQL.StatementResult=##class(%SQL.Statement).%ExecDirect(,sql)
  ..RSToDynamicArray(.rs).%ToJSON()
}

}

Result:

USER>##class(dc.test).Test()
[{"fStr":"hello","fInt":2,"fDate":"2026-03-05","fBool":true,"fNull":null,"fClob":"Hello","fBlob":"\u0000\u0001\u0007"}]

PS: By the way, in version 2016.3, this could be done much more easily:

write rs.$compose("%Array").$toJSON()

Unfortunately, I have not found a way to replicate this code in version 2025.3

I do not know if multidimensional arrays are acceptable in your case or only one-dimensional arrays, so here is a general example.

Class dc.test Abstract ]
{

ClassMethod Foo(glbName)
{
  set ref = $query(@glbName@(""),-1)
  set @ref@("end") = "end"
}

ClassMethod Test()
{
  ^||A
  ^||A($c(65535)) = ""
  ^||A("DEF") = ""
  ^||A(123) = ""
  ^||A("ABC") = ""
  ..Foo("^||A")
  zw ^||A
}

}

USER>##class(dc.test).Test() ^||A(123)="" ^||A("ABC")="" ^||A("DEF")="" ^||A($c(65535))="" ^||A($c(65535),"end")="end"

You are not passing the pDate parameter to the request anywhere in the code.

Instead of

set sqlQuery "SELECT EMPLID, MedCtrID, LastName, FirstName, EFFDT FROM OSU_Workday_SQL.PDMTerminations WHERE EFFDT > :pDate"

set rset tStatment.%Execute()

need

set sqlQuery "SELECT EMPLID, MedCtrID, LastName, FirstName, EFFDT FROM OSU_Workday_SQL.PDMTerminations WHERE EFFDT > ?"

set rset tStatment.%Execute(pDate)

I would like to clarify once again that the InterSystems JDBC driver itself returns its current limits on the maximum size of certain types of data that can be viewed by any JDBC client (DbVisualizer, SQuirreL, etc.).

Let's check together in practice the maximum size of LONGVARCHAR in the InterSystems JDBC driver.

Class dc.test Extends %Persistent  
{  
Property Note As %Stream.GlobalCharacter;  
}
import java.io.*;
import java.sql.*;
class IRISDemo
{
    public static void main(String[] args)
    {
        Connection db = null;
        try
        {
            File file = new File("C:\\big.iso");
            FileInputStream fis = new FileInputStream(file);
            InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
            BufferedReader br = new BufferedReader(isr);
            Class.forName("com.intersystems.jdbc.IRISDriver");
            DriverManager.setLoginTimeout(3);
            db = DriverManager.getConnection("jdbc:IRIS://localhost:1972/USER", "_SYSTEM", "SYS");
            PreparedStatement ps = db.prepareStatement("update dc.test set Note=? where id=1");
            ps.setCharacterStream(1, br, file.length());
            System.out.printf("Result = %s", ps.executeUpdate()).println();
            ps.close();
            db.close();
            System.out.println("OK!");
        } catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

If the file size exceeds 2147483647 characters, the following error occurs:

java.sql.SQLException: Stream too long: 2895740928 at com.intersystems.jdbc.IRISPreparedStatement.setCharacterStream(IRISPreparedStatement.java:889)

Where is defined/documented the JDBC LONGVARCHAR maximum length? My search cannot find any reference on max size of 2147483647

LONG VARCHARORACLE

The LONG VARCHAR data type stores data from and into an Oracle AI Database LONG column.

The first 4 bytes of a LONG VARCHAR contain the length of the item. So, the maximum length of a stored item is 2^31-5 bytes.
BLOBs and CLOBsIRIS

BLOBs and CLOBs can store up to 4 Gigabytes of data (the limit imposed by the JDBC and ODBC specifications). The default data types that store BLOBs and CLOBs in InterSystems SQL are automatically compressed.

Although the documentation differs from what the IRIS JDBC driver returns, in this case I tend to believe the driver more than the documentation ;)

In addition, in the %AbstractStream class, the MAXLEN parameter is 2147483647.

I need to reproduce the same interface/definition as currently implemented in Oracle that is using a CLOB column.

In IRIS JDBC, the CLOB type is not provided by design: OdbcType (Class Keyword)

But this is not a problem, as the Oracle and IRIS drivers support transparent transformations for streaming data types:

See

If you run the following code, then the type of variables a and b in the generated class will be %Stream.GlobalCharacter

create table dc.test
(
LONGVARCHAR,
b CLOB
)
In addition, I'm no expert in JDBC but wit seems that LONGVARCHAR has a Maximum Length 32,700 characters in JDBC, not quite enough for an arbitrary stream.
Max Size for LONGVAR(BINARY/CHAR) = 2147483647, for VAR(BINARY/CHAR) = 4096

PS: By the way, in DbVisualizer you can view all types supported by the InterSystems IRIS JDBC driver, their numeric codes, maximum sizes, and much more.

Same thing, just different:

set true="1",
    false=0,
    j={}

do j.%Set("a",1,"boolean")
do j.%Set("b",0,"boolean")
set j.c=true
set j.d=false
  
set iter j.%GetIterator()
while iter.%GetNext(.key, .value, .type {
  write "key = "_key_", value = "_value_", type = "_type,!
}

Output:
key = a, value = 1, type = boolean
key = b, value = 0, type = boolean
key = c, value = 1, type = string
key = d, value = 0, type = number

Actually, my another proposal is to have true and false as part of the language, where true=1 and false 0 or similar for booleans.

Isn't that the case now?

set true="1",
    false=0,
    j={"a":true,"b":false,"c":(true),"d":(false)}

set iter j.%GetIterator()
while iter.%GetNext(.key, .value, .type {
  write "key = "_key_", value = "_value_", type = "_type,!
}

Output:
key = a, value = 1, type = boolean
key = b, value = 0, type = boolean
key = c, value = 1, type = string
key = d, value = 0, type = number

@Evgeny Shvarov

Unfortunately, I have not yet found a documented and [Not Internal] way out of the box to see not only the text of the request, but also the values of its input parameters.

PS: take a look at the methods of the %SYSTEM.SQL.xDBC class. Here is an example of the log for the query above (from DbVisualizer):

SQL text (raw from client): 1 lines
line 1: SELECT * FROM del . a WHERE id < :%qpar(1)
Var Types: Parameter 1: Constant of type INTEGER
INPUT params:
%qpar(1) = 3

If auditing is enabled, you can see the query without the actual parameter values.
To copy the query to the SQL query tool, the query will also have to be cleaned of garbage.

For example, there is the following query

select * from del.a where id<3

From SMP:

Event: DynamicStatementQuery
Event Data: SELECT * FROM del . a WHERE id ?

From DbVisualizer:

Event: XDBCStatementQuery
Event Data: SELECT * INTO :i%%col1:i%%col2:i%%col3 FROM del . a WHERE id :%qpar(1)

Since you used the migration wizard, I assume that the problem is due to the primary key ID and the indexes on it.

Can you use the wizard to create a test table with only two fields, ID and row_status, and post the source code of the generated class here? And more: how many records are there in the table?

Enable auditing for %System/%SQL/DynamicStatementQuery, %System/%SQL/EmbeddedStatementQuery, %System/%SQL/XDBCStatementQuery system events.

You can view last actual values of the passed parameters of SQL query in SMP: System > Processes > Process Details > Variables

I'm curious why LPAD, RPAD and similar string functions are not exposed as $SYSTEM.SQL.Functions class members
This is a good question that I would also like to get an answer to.

LPAD is a SQL function, not an ObjectScript function, which is why it didn't work for you if you replaced the single quotes with double quotes.

USER>write ">"_LPAD(1,10,"0")_"<"
><
USER>write ">"_$$lpad^%qarfunc(1,10,"0")_"<"
>0000000001<

There are two ways to solve the problem: using embedded SQL or an analog of LPAD for ObjectScript, for example:

CREATE TABLE example_table (
    id VARCHAR(10PRIMARY KEY,
    normalized_id VARCHAR(10COMPUTECODE {
        &sql(select LPAD(:{id}, 10, '0'into :{*})
    }
    COMPUTEONCHANGE(id))

This should be checked on your environment, depending on the versions of Caché, Windows, and Microsoft SQL Server used. I have already provided the code for this above.

I would advise you to first make sure that you are using the latest version of Caché (and the ODBC/JDBC driver), which has fixed many bugs, including various memory leaks: Caché® and Ensemble® Change Notes (2018.1.11)

In addition, you may find the New Method $SYSTEM.Util.CleanDeadJobs() useful.

Once upon a time, when the grass was greener and the sky was bluer, there was even an add-in application Caché RoseLinkPDF.