Question
· Apr 1

IRIS Enable Long Strings

On IRIS version 2024.1.0.262.0 I have a table that store streams as %GlobalCharacterStream.

But depeding on the blob being saved I get the error: [Error: <<MAXSTRING>]

I had that with Caché, but to solve that I'd just enable the long strings on the page System Administration > Configuration > System Configuration > Memory and Startup > Enable Long Strings.

But for Iris I can't find that configuration, I looked on every config page from the portal and couldn't find it.

Btw this blobs are stored on Caché and the system using IRIS is reading from Caché and saving on IRIS.

Where, on IRIS, can I make the same configuration of Long Strings that exists on Caché?

Product version: IRIS 2023.3
$ZV: 2024.1.0.262.0
Discussion (15)4
Log in or sign up to continue

When Inserting on a table with the following SQL:

INSERT INTO ERP.Arquivos (Blob, Descricao, FileName, MimeType, IDIntegracao, Updated, UpdateUser, Inserted, InsertUser) VALUES ({Stream}, 'some Description', 'filename.jpg', 1, 10, CURRENT_TIMESTAMP, 'username', CURRENT_TIMESTAMP, 'username')

Inner Exception: [SQLCODE: <-400>:<Ocorreu um erro fatal>]
[Error: <<MAXSTRING>zExecute+5^%sqlcq.RODOFORCE4.cls54.1>]
[Location: <ServerLoop>]

----------------------------

This SQL works fine when dealing with small streams, but on some larger files I get the error above.

I figured it was the long strings because we had the problem with large SQL's on Caché and we needed to enable the Long Strings Config.

Just to see things clear, the size of a stream is limited by the size of the database (some GBs or even TBs) and the size of the storage media (hdd,sdd), whichever smaller is (less the size of other globals). The size of a table row (all fields together) is limited by the string size (either 32KB or 3.6MB). In a table row you can put a REFERENCE of a stream but not the stream itself.

The Stream is a Blob of an image that has 2.830KB

This blob was inserted on Caché the exacly same way and works, but for that we had to enable the Long Strings config.

When trying the same SQL on IRIS I'm getting the MAXSTRING error, that I suppose it's because the Long Strings config is not enabled.

Please correct me if I'm wrong, but I really don't see any other thing that could be causing that.

It's the first time that I'm using IRIS so it's very possible that I'm missing something, but considering previous experiences with Caché the first thing that comes to my mind is this config.

When looking on IRIS documentation I only find the path "System Administration > Configuration > System Configuration > Memory and Startup > Enable Long Strings."
But when oppening it on IRIS Management Portal the "Enable Long Strings" config just does not exist.

I noticed that you are using the {Stream} syntax, which is typical for trigger code or calculated field.
Could you check the following code at your place:

Class dc.test Extends %Persistent
{

Property stream As %GlobalCharacterStream;

Trigger NewTrigger1 [ Event = INSERT ]
{
 i $IsObject({stream}{
   t={stream}
   ^||tmp=t.Size
 }
}

ClassMethod Test()
{
  ..%KillExtent(,$$$YES)
  
  f=##class(%Stream.FileBinary).%New()
  f.LinkToFile("C:\test.jpg")
  
  &sql(insert into dc.test(streamvalues(:f))
  'SQLCODE {
    &sql(select CHAR_LENGTH(streaminto :len from dc.test where %id=1)
    len," ",^||tmp,!
  }
}

}

I have saved a 16 MB file without any problems:

USER>##class(dc.test).Test()
16446809 16446809

Hi Antonio,

Are you using the IRISClient assembly from your IRIS 2024.1.0? 

I have Visual Studio 2022, I am using InterSystems.Data.IRISClient.dll from my C:\InterSystems\IRIS20241\dev\dotnet\bin\net7.0.

I have an image file 

03/28/2024  09:28 AM         5,261,669 myimage.png

I use following sample .Net app that I borrow from Cache 2018 and modify all Cache references with IRIS references, I have no problem inserting the 5.2MB image file into a blob field in my test table.

Harry

using System;
using System.IO;

// Add the following using statement
using InterSystems.Data.IRISClient;

namespace C_SharpConsoleExample
{
    /// <summary>
    /// Summary description for Class1.
    /// </summary>
    class ConsoleApp
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            //
            // TODO: Add code to start application here
            //

            // Create a connection to Cache
            IRISConnection conn = new IRISConnection();

            // Cache server Connection Information
            // Set Server to your IP address and port to Cache SuperServer port, Log File is optional
            conn.ConnectionString = "Server = localhost; Log File=cprovider.log;Port=1972; Namespace=USER; Password = SYS; User ID = _SYSTEM;";

            //Open a Connection to Cache
            conn.Open();

            // Create table with streams
            IRISCommand dropCmd = new IRISCommand("drop table Sample.Streams", conn);
            try
            {
                dropCmd.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                //Do nothing
                string test = ex.Message;
            }

            // Create table with streams
            IRISCommand cmd = new IRISCommand("create table Sample.Streams(binaryData LONGVARBINARY, characterData LONGVARCHAR)", conn);
            cmd.ExecuteNonQuery();

            // Insert stream data into table
            cmd.CommandText = "insert into Sample.Streams(binaryData, characterData) values (?, ?)";

            // One way to mark the parameter as a binary stream datatype and then set the Value
            cmd.Parameters.Add(new IRISParameter("binaryData", IRISDbType.LongVarBinary));
            cmd.Parameters[0].Value = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

            // An alternate way to bind the data to the parameter and then mark the parameter as a character stream
            cmd.Parameters.Add(new IRISParameter("characterData", (string)"This is a short character stream!"));
            cmd.Parameters[1].IRISDbType = IRISDbType.LongVarChar;

            cmd.ExecuteNonQuery();

            // Now insert two files from disk as streams
            // Open binary file and read into byte[]
            // FileStream fbs = new System.IO.FileStream(".\\ConsoleStream.exe", System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite);
            FileStream fbs = new System.IO.FileStream(".\\myimage.png", System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite);
            int filebLen = (int)fbs.Length;
            byte[] filebData = new byte[filebLen];
            fbs.Read(filebData, 0, (int)filebLen);
            fbs.Close();
            cmd.Parameters[0].Value = filebData;
            cmd.Parameters[0].Size = filebLen;

            // Open character file and read into string
            StreamReader fcs = new StreamReader(new System.IO.FileStream(".\\cprovider.log", System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite));
            string filecData = fcs.ReadToEnd();
            int filecLen = filecData.Length;
            fcs.Close();
            cmd.Parameters[1].Value = filecData;
            cmd.Parameters[1].Size = filecLen;

            cmd.ExecuteNonQuery();

            cmd.Parameters.Clear();

            // Create an SQL Statement to execute (Command)
            cmd.CommandText = "select ID,* from Sample.Streams";

            // Execute and fetch the data from Cache
            IRISDataReader reader = cmd.ExecuteReader();
            Console.WriteLine("Output from statement: " + cmd.CommandText);
            Console.WriteLine(" ");
            Console.WriteLine("ID\tbinaryStream\t\tcharacterStream ");
            while (reader.Read())
            {
#if BYNUMBER
                // Access Column by Number
                Console.Write(reader[0]);
                Console.Write("\t");
                Console.Write(reader[1] + " : N/A");
                Console.Write("\t");
                Console.Write(reader[2]);
                Console.Write("\t");
                Console.WriteLine();
#else
                // Access Column by Name
                Console.Write(reader[reader.GetOrdinal("ID")]);
                Console.Write("\t");
                Console.Write(reader[reader.GetOrdinal("binaryData")] + " : N/A");
                Console.Write("\t");
                Console.Write(reader[reader.GetOrdinal("characterData")]);
                Console.Write("\t");
                Console.WriteLine();
#endif
            }
            Console.WriteLine("");
            Console.WriteLine("End of characterStream output!");

            // Cleanup Reader, Connection and Command
            reader.Close();
            cmd.Dispose();
            conn.Close();
        }
    }
}