I decided to try this yesterday, and I got a much bigger boost in performance than expected. The query was very simple, just a select distinct fieldname from table order by fieldname asc. The table contained about half a million records, but there are only about 350 distinct values in this field. Without that order by clause, this query always ran pretty quickly, around .013 seconds, but with the order by on the end, it was taking over 8 seconds to run. I tuned that table, and now even with the order by it's running in about .015 seconds. I was not expecting that drastic of a difference on such a simple query!

I think what you're looking for might be setting the isolation level to read committed. This will make the process wait for the in-progress changes have been committed, though you'll still want to make sure you handle SQLCODE -114 somehow, too. That's the code you get back if there's a timeout waiting for the lock. You should be able to set that using:

%SYSTEM.SQL.Util.SetOption("IsolationMode",1,.oldval)

If you do that before your query, the rest of the process will run at that isolation level.

You can use that same method to set the LockTimeout too, by the way. Default is 10 seconds.

Ideally, that would be the case, but some of these aren't apps we've written and are forcing to log in with a safe user account. They're 3rd-party apps that our users are logging into using their usual credentials to create an ODBC connection. Most of them are reporting tools, but a few are also capable of running queries other than selects.

I think what we'd like to be able to do is determine roles similarly to how you can set up application roles so that the user gets those roles when they log in, we set permissions based on the program being used. Can we do that somehow in ZSTART?

Your tResult will have a property called %SQLCODE that gets set when the query is run. If %SQLCODE = 100, that means that the query ran successfully but returned no results or that you've reached the end of the result set. If %SQLCODE = 0, you have some results. If %SQLCODE is less than zero, that's an error code.

if tResult.%SQLCODE = 100{

//whatever you want to do for no results here

}

If you're encoding your data before sending it, you have to specify how it was encoded in a content encoding header so that the server you're sending data to knows how to decode it.

I think it's more likely, though, that it's an issue with your content type. Where you're setting it to "text/plain", if it's supposed to be json, you might try setting it to "application/json" instead.

If the file isn't accessible to link to directly, you may want to look into extending the %CSP.StreamServer class and linking to that. At a bare minimum, you'll want to override the OnPage and OnPreHTTP methods:

ClassMethod OnPage() As %Status
{
set myfile = ##class(%File).%New("/path/to/file")
do myfile.Open("S")
do myfile.OutputToDevice()
quit $$$OK
}

ClassMethod OnPreHTTP() As %Boolean [ Language = cache ]
{
do %response.SetHeader("Content-Disposition","attachment;filename=""filename""")
quit 1
}

Of course using your own file name and the path to the file. That's the local computer file path, not a URL. You also should set the content type appropriately using set %response.ContentType = "text/csv" or whatever the MIME type of the file is so that the browser can identify it correctly.

Unless you want to have to write another %CSP.StreamServer for every file, you'll have to pass the name of the filepath as an argument. So that would look more like:

ClassMethod OnPage() As %Status
{
set myfile = ##class(%File).%New(%request.Get("filepath"))
do myfile.Open("S")
do myfile.OutputToDevice()
quit $$$OK
}

ClassMethod OnPreHTTP() As %Boolean [ Language = cache ]
{
set filepath = %request.Get("filepath")
set %response.ContentType = "text/csv" //or whatever the appropriate MIME type is
do %response.SetHeader("Content-Disposition","attachment;filename="""_$P(filepath,"/",*)_"""")
quit 1
}

Then you could link to it as whatever the path to your stream server is with ?filepath=/path/to/file on the end.

If you take that approach, though, do some validation on the filepath and make sure it can ONLY go to the folder you want! Or, only pass the filename as a parameter to the page, and hard-code the folder in those methods.