Mike Kadow · May 7, 2016

%SYS.Journal.File_Search() - how does it accept parameters?

I am trying to execute a system query. - %SYS.Journal.File_Search()

The Query is looking for a Journal File Name and a String.

However, the Query line accepts no parameters. - Query Search() As %Query(ROWSPEC = "Offset:%Integer")

But in the SearchExecute method if checks for File and String.

ClassMethod SearchExecute(ByRef qHandle As %Binary, String As %String, FileName As %String . . .
                i $g(String)="" q $$$ERROR($$$JournalFileSearchUndefined)
                i $g(FileName)="" q $$$ERROR($$$JournalFileUndefined)

Can anyone help me understand how the String and FileName is passed into the query?

0 747
Discussion (5)1
Log in or sign up to continue

You could just pass this arguments in Execute method, something like this

    new $namespace
    set $namespace="%sys"

    Set tRS = ##class(%ResultSet).%New("%SYS.Journal.File:Search")
    Set tSC = tRS.Execute("test", "/opt/cache/mgr/journal/20160507.001")
    if $$$ISERR(tSC) {
        do $system.OBJ.DisplayError(tSC)
    do tRS.%Display()

this code will return all offset's which contains text - 'test'

You must remember that this query should be executed only in %SYS namespace.

Usually we could use some another modern ways, which allow us to call queries.  

    set statement=##class(%SQL.Statement).%New()
    set tSC=statement.%PrepareClassQuery("%SYS.Journal.File","Search")
    if $$$ISERR(tSC) {
        do $system.OBJ.DisplayError(tSC)
    Set tRS = statement.%Execute("test", "/opt/cache/mgr/journal/20160507.001")

or even much shorter

    set tRS=##class(%SYS.Journal.File).SearchFunc("test", "/opt/cache/mgr/journal/20160507.001")
    do tRS.%Display()

But unfortunately such code snippets does not work in case when Query does not contain any parameters in his original declaration.

As for me, when I have to investigate some strange behavior in my application, I prefer to read all journal files or limited by time, and then all records, and then I have some some to find exactly what I need, like only kill records or only sets, and only for some concrete global references. So, my usual code looks like this:

     set exit=0
    set docId="14105401"
    set jrnFile=##class(%SYS.Journal.System).GetCurrentFileName()
    do {
        set jrn=##class(%SYS.Journal.File).%OpenId(jrnFile)
        #dim rec As %SYS.Journal.Record = jrn.FirstRecord
        set last=jrn.End
        set pr=0,opr=0
        do {
            set pr=$j(rec.Address/last*100,0,2)
            if pr'=opr write $c(13),pr_"%" set opr=pr
            if rec.%IsA("%SYS.Journal.SetKillRecord") {
                set glb=rec.GlobalNode
                if $qs(glb, 0),"^My.Global",$qs(glb, 1)=docId {
            set rec=rec.Next
            read q:0 set exit=q="q"
        while $isobject(rec)
        set sc=jrn.GetPrev(jrnFile, .jrnFile)
    while $$$ISOK(sc)

This code looks all journal files, file by file from latest to oldest, and show progress for current journal file, and shows offset if it found something interesting

I must say, interesting work-arounds, thank you.

Do you think this class was created this way on purpose,

or was this a mistake, not having the parameters on the query line?

In my opinion, it's more looks like by mistake, not sure why it could be done in other ways, it still working, with some limitations but any way.
We can wait what would say InterSystems, may be they will fix it in future.

This is pretty clearly a mistake in the definition of the Search custom query.  We will look into the history a bit more and correct it. Since the (custom query) Execute method defines the expected arguments, invocation through a resultset works.  Beyond the understandable confusion you had, Mike, it makes sense that this could cause other things not to work like Dmitry illustrates.

You might want to take a look at the List query in %SYS.Journal.Record.  That's a much nicer interface for searching a journal in my opinion.  Also, I suspect you'll find it performs better for most use cases.