Written by

Senior Cloud Architect at InterSystems
Question Eduard Lebedyuk · Jan 15, 2020

How to determine that a package exists?

I need to know if given package exists or not.

Currently found two solution - one doesn't work, another works but I don't like it.

Solution 1.

I started, of course, with %Dictionary package - it has PackageDefinition class after all.

However, %ExistsId returned 0 on packages that clearly do exist, so I went to %LoadData, it uses this macro to determine if the package exist:

#define PACKAGE(%package)             ^oddPKG($zcvt(%package,"u"))

And zw ^oddPKG showed the root cause - ^oddPKG global only contains data for packages with tables (or something along these lines).

Solution 2.

Using sql to find first class that starts with package name. Seems like an overhead to me.

Query PackageExists(package) As %SQLQuery
{
SELECT TOP 1 1 As "Exists"
FROM %Dictionary.ClassDefinition
WHERE ID %STARTSWITH :package
}

/// write ##class().GetPackageExists("isc")
ClassMethod GetPackageExists(class)
{
    #dim exists As %Boolean
    if class = "" {
        set exists = $$$YES
    } else {
        set package = $case($l(class, "."), 1: class, : $p(class, ".", 1, *-1))
        set rs = ..PackageExistsFunc(package)
        do rs.%Next()
        set exists = (rs.Exists = $$$YES)
    }    
    quit exists
}

Ideas?

Comments

Timothy Leavitt · Jan 15, 2020

How about (swapping in ^oddDEF if you don't care if it's been compiled):

ClassMethod PackageExists(package) As %Boolean
{
    Set prefix = package_"."
    Set firstClass = $Order(^oddCOM(prefix))
    Quit prefix = $Extract(firstClass,1,$Length(prefix))
}
0
Julius Kavay · Jan 15, 2020

The simple (and documented) answer is something like this:

set name="Test.Package"

if ##class(%Dictionary.PackageDefinition).GetPackageList().Find($zconvert(name,"u")) { w "Yes" } else { w "No" }

@Timothy

using ^oddDEF and/or ^oddCOM works today, we hope, it will work tomorrow too, but there is no garantie

0
Eduard Lebedyuk  Jan 15, 2020 to Julius Kavay

%Dictionary.PackageDefinition does not contain all packages, only ones with immediate tables (see Solution 1).

If it did, calling %ExistsId  would be enough.

0
Julius Kavay  Jan 15, 2020 to Eduard Lebedyuk

In the class definition one can create  persistent-, serial-, registered-, abstract- and data-classes.

All of the above classes are contained in ##class(%Dictionary.PackageDefinition).GetPackageList(). Do you have a example for an class, which is not contained in the above method?

Or there is just a misunderstanding?

0
Eduard Lebedyuk  Jan 15, 2020 to Julius Kavay

Create this class:

Class ABC.Try
{

/// w ##class(ABC.Try).PackageExists()
ClassMethod PackageExists(package = "ABC") As %Boolean [ CodeMode = expression ]
{
##class(%Dictionary.PackageDefinition).%ExistsId(package)
}

}

Test:

>w ##class(ABC.Try).PackageExists()
0

It also won't be available in GetPackageList

ABC.Try can also extend registered or persistent to the same effect.

0
Julius Kavay  Jan 15, 2020 to Eduard Lebedyuk

Ok, I started my Studio (2018.1.1) --> New Class --> ABC.Try --> Finish.

Then removed the Extends... and I left over with

Class ABC.Try
{

}

Then saved (but no compile) and my test in a terminal:

USER>s aa=##class(%Dictionary.PackageDefinition).GetPackageList()

USER>w aa.Find("ABC")
523
USER>

0
Eduard Lebedyuk  Jan 16, 2020 to Julius Kavay

Thanks!

Turns out PackageDefinition Queries are not based on PackageDefinition class.

0
Vitaliy Serdtsev · Jan 16, 2020

@Julius Kavay got a point.

I will offer two more options:

<FONT COLOR="#000080">Class ABC.Try </FONT><FONT COLOR="#000000">{

</FONT><FONT COLOR="#000080">/// d ##class(ABC.Try).PackageExists() ClassMethod </FONT><FONT COLOR="#000000">PackageExists(</FONT><FONT COLOR="#ff00ff">package </FONT><FONT COLOR="#000000">= </FONT><FONT COLOR="#800080">"ABC"</FONT><FONT COLOR="#000000">) {   </FONT><FONT COLOR="#008000">; option by Julius Kavay   </FONT><FONT COLOR="#0000ff">s </FONT><FONT COLOR="#800000">list</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#000080">##class</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008080">%Dictionary.PackageDefinition</FONT><FONT COLOR="#000000">).</FONT><FONT COLOR="#0000ff">GetPackageList</FONT><FONT COLOR="#000000">()   </FONT><FONT COLOR="#0000ff">w </FONT><FONT COLOR="#000000">''</FONT><FONT COLOR="#800000">list</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#0000ff">Find</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#0000ff">$zcvt</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#800000">package</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#008000">"U"</FONT><FONT COLOR="#000000">)),!      </FONT><FONT COLOR="#008000">; option 2   </FONT><FONT COLOR="#0000ff">w </FONT><FONT COLOR="#000080">##class</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008080">%Library.RoutineMgr</FONT><FONT COLOR="#000000">).</FONT><FONT COLOR="#0000ff">Exists</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#0000ff">$zcvt</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#800000">package</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#008000">"U"</FONT><FONT COLOR="#000000">)_</FONT><FONT COLOR="#008000">".PKG"</FONT><FONT COLOR="#000000">),!      </FONT><FONT COLOR="#008000">; option 3   </FONT><FONT COLOR="#0000ff">k </FONT><FONT COLOR="#800000">list   </FONT><FONT COLOR="#0000ff">d $system</FONT><FONT COLOR="#008080">.OBJ</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#0000ff">GetPackageList</FONT><FONT COLOR="#000000">(.</FONT><FONT COLOR="#800000">list</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">package</FONT><FONT COLOR="#000000">)   </FONT><FONT COLOR="#0000ff">w </FONT><FONT COLOR="#000000">''</FONT><FONT COLOR="#0000ff">$d</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#800000">list</FONT><FONT COLOR="#000000">),! }

}</FONT>

USER><FONT COLOR="#0000ff">d </FONT><FONT COLOR="#000080">##class</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008080">ABC.Try</FONT><FONT COLOR="#000000">).</FONT><FONT COLOR="#0000ff">PackageExists</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008000">"ab"</FONT><FONT COLOR="#000000">)</FONT> 0 0 0

USER><FONT COLOR="#0000ff">d </FONT><FONT COLOR="#000080">##class</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008080">ABC.Try</FONT><FONT COLOR="#000000">).</FONT><FONT COLOR="#0000ff">PackageExists</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008000">"abc"</FONT><FONT COLOR="#000000">)</FONT> 1 1 1

UPD: Take another look at the %Dictionary.PackageDefinitionQuery:SubPackage/FlatPackage, %ZEN.Utils:EnumeratePackages

0
Oliver Thompson · Sep 5, 2024

I have just had to do something similar, but needed it to be Case Sensitive.
The only solution that I could find that would be Case Sensitive was the SQL solution.

0
Dan Pasco · Sep 5, 2024

The PackageDefinition class contains only packages where we need to keep metadata about a package. Most packages do not have any extra metadata.

The SQL solution is the correct solution IMO. If I were a developer on the Object team I would advocate for a method in $system.OBJ - PackageExists(<package_name>) perhaps. :)

0