Caché Programming Orientation Guide
Introduction to Caché Programming
[Back] [Next]
Go to:

This book is intended as an introduction for programmers who are not familiar with Caché or who are familiar with only some kinds of Caché programming. It is not a tutorial but rather a survey of the elements in the Caché toolkit, with information on how these elements fit together. After reading this book, you should have an idea of the options available to you and where to find more information on those options.

For this book, the emphasis is on server-side programming, rather than client-side programming. Via various Caché language bindings, a Caché server can work with clients written in many different languages. If you are creating or maintaining such clients, most of this book is probably inapplicable to you.
This chapter provides a high-level overview of the language elements you can use in Caché server-side programs and shows how Caché combines them. It discusses the following topics:
Caché is a high-performance object database with several built-in general-purpose programming languages. It supports multiple processes and provides concurrency control. Each process has direct, efficient access to the data.
In Caché, you can write routines, classes, or a mix of these, as suits your preferences and the history of your application. In all cases, stored data is ultimately contained in structures known as globals (a later section in this chapter discusses these). Caché programming has the following features:
Older Caché applications consist entirely of routines, because these applications were written before Caché supported class definitions. In contrast, some newer applications are written almost entirely in classes. You can choose the approach that is appropriate for your needs.
When you create routines in Caché, you can choose the programming language for each routine. The choices are as follows:
Because it is useful to see what these languages look like, the following shows part of a routine written in Caché ObjectScript:
    SET text = ""
    FOR i=1:5:$LISTLENGTH(attrs)
        IF ($ZCONVERT($LIST(attrs, (i + 1)), "U") = "XREFLABEL")
            SET text = $LIST(attrs, (i + 4))
    IF (text = "")
        QUIT $$$ERROR($$$GeneralError,$$$T("Missing xreflabel value"))
The following shows part of a routine written in MVBasic:
BAD.LST = ''
* If there is an active select list use it, otherwise get file names from the VOC
The following shows part of a routine written in Caché Basic:
' display an ordered list of matches
' user can enter full or partial name, full or partial phone, or a valid date
' pick from a list of matches, and EDIT their choice
Option Explicit
dim id, name, phone, intdob, matches
public sub main()
        dim done
                getsubmit(id, done) ' let user submit a string for lookup
                if id = 0 then continue do
                display(id, "table") ' display the chosen person
                edit(id) ' edit the chosen person
        loop until done
end sub
private sub getsubmit(ByRef id as %Integer, ByRef done as %Boolean)
' ask user what to search for, and take appropriate action
        dim submit   
        id = 0 : done = False
        println : input "Lookup: ", submit : println
        if (submit = "") then ' user entered nothing
                done = True
                exit sub 
        end if
        ' figure out what user entered
The next chapter provides an introduction to Caché ObjectScript. This book does not discuss Caché MVBasic or Caché Basic in any detail.
Caché also supports classes. You can use the system classes and you can define your own classes.
In Caché, a class can include familiar class elements such as properties, methods, and parameters (known as constants in other class languages). It can also include items not usually defined in classes, including triggers, queries, and indices.
The following shows a class definition:
Class Sample.Employee Extends Person

/// The employee's job title.
Property Title As %String(MAXLEN = 50);

/// The employee's current salary.
Property Salary As %Integer(MAXVAL = 100000, MINVAL = 0);

/// A character stream containing notes about this employee.
Property Notes As %Stream.GlobalCharacter;

/// A picture of the employee
Property Picture As %Stream.GlobalBinary;

/// This method overrides the method in the Person class.
Method PrintPerson() 
    Write !,"Name: ", ..Name, ?30, "Title: ", ..Title

For each method, you can specify the programming language to use in the definition of that method, although typically all methods in a class use the same language, for simplicity. The default is Caché ObjectScript; the next chapter provides an introduction to it. The other choices are Caché MVBasic, Caché Basic, and Transact-SQL. (There are additional options for client-side programming, not discussed in this book.)
You can use classes from within routines. For example, the following shows part of a routine, in which we refer to the Sample.Person class:
 //get details of requested person; print them
showperson() public {
    set rand=$RANDOM(10)+1        ; rand is an integer in the range 1-10
    write "Your random number: "_rand
    set person=##class(Sample.Person).%OpenId(rand)  
    write !,"This person's name: "_person.Name
    write !,"This person's age: "_person.Age
    write !,"This person's home city: "_person.Home.City
Similarly, a method can invoke a label in a routine. For example:
Method DemoRoutineCall(input) as %String
    Set value=$$function^myroutine(input)
    Quit value
The chapter Basic Ideas in Class Programming provides a brief introduction to class programming, for the benefit of readers who have not done this kind of programming. The chapters after that discuss classes in Caché and the unique capabilities of persistent classes in Caché.
Introduction to Globals
Caché supports a special kind of variable that is not seen in other programming languages; this is a global variable, which is usually just called a global. In Caché, the term global indicates that this data is available to all processes accessing this database. This usage is different from other programming languages in which global means “available to all code in this module.”
The contents of a global are stored in a Caché database. The next chapter introduces these more thoroughly; for now, it is important just to know the following points:
Ways to Access Data
In Caché, a database contains globals and nothing else; even code is stored in globals, as described later in this book. At the lowest level, all access to data is done via direct global access — that is, by using commands and functions that work directly with globals.
Many currently running applications were developed long before Caché included support for classes. Some of these applications use direct global access. Other applications use custom APIs such as FileMan, which is in the public domain. These APIs, of course, internally use direct global access.
When you use persistent classes, discussed later in this book, you can create, modify, and delete stored data in either of the following ways:
Internally, the system always uses direct global access.
Because object classes provide a more controlled interface, and because Caché persistent classes are projected to tables that can be queried via SQL, it is often desirable to add a class interface to existing applications. You can do so, if you understand the structure of the globals.
Implications of Using Globals
Globals are stored physically in a highly optimized structure, and the code that manages this structure is separately optimized for every platform that Caché runs on. These optimizations ensure that operations on globals have high throughput (number of operations per unit of time), high concurrency (total number of simultaneous users), efficient use of cache memory, and require no ongoing performance-related maintenance (such as frequent rebuilding, re-indexing, or compaction). The physical structure used to store globals is completely encapsulated; it is generally unnecessary to consider the physical data structure.
Global storage is sparse, meaning that only nodes with data values are stored in the database. This means that Caché often requires less than half of the space needed by a relational database.
Other benefits of globals include the following:
Caché SQL
As noted previously, Caché provides an implementation of SQL, known as Caché SQL.
You can use Caché SQL within routines and within methods. To use SQL in these contexts, you can use either or both of the following tools:
Caché ObjectScript also supports macros, which define substitutions. The definition can either be a value, an entire line of code, or (with the ##Continue directive) multiple lines. You use macros to ensure consistency. For example:
 #define StringMacro "Hello, World!"

 write $$$StringMacro
To give you an idea of what can be done in macros, the following example shows the definition of a macro that is used internally:
 #define output1(%str,%lf,%indent) do output^%fm2class(%str,%lf,%indent,$$$display) 
This macro accepts an argument, as many of them do. It also refers to another macro.
You can use macros in routines as well as in classes. Some of the system classes use them extensively.
Include Files
You can define macros in a routine and use them later in the same routine. More commonly, you define them in a central place. To do this, you create and use include files. An include file defines macros and can include other include files.
The following shows parts of a system include file:
/// Create a success %Status code
#define OK                     1
/// Return true if the %Status code is success, and false otherwise
/// %sc - %Status code
#define ISOK(%sc)              (+%sc)
/// Return true if the %Status code if an error, and false otherwise
/// %sc - %Status code
#define ISERR(%sc)             ('%sc)
Here is another include file, in entirety:
#include %occCacheDirect
#include %occExtent
#include %occTransaction
#include %occInclude
#include %msql
#include %cspInclude
Then you can do the following:
Note that you use slightly different syntax to include an include file within a class definition. See Macros and Include Files in Class Definitions,” later in this book.
How These Code Elements Work Together
It is useful to understand how Caché uses the code elements introduced in this chapter.
The reason that you can use a mix of Caché ObjectScript, Caché SQL, Caché MVBasic, macros, class definitions, routines, and so on is that Caché does not directly use the code that you write. Instead, when you compile your code, Caché generates the code that it uses, which is OBJ code, used by the Caché virtual machine.
There are multiple steps; the preceding figure gives a general idea of them. It is not necessary to know the steps in detail, but the following points are good to remember:
All routines and class definitions are stored in the same Caché databases as the generated code. This fact makes the code easier to manage. Caché provides a robust set of source control hooks for Studio that InterSystems developers have used for many years. You can use these hooks as well; for an introduction, see Development Tools,” later in this book.