Removing account-specific code for I-descs

3 views
Skip to first unread message

Dawn Wolthuis

unread,
May 28, 2008, 9:50:00 AM5/28/08
to intersystems-mv
We are currently hand-coding our I-descs in three parts within a
persistent class as below. We have to code using two passes so that
we check the location of the compiled I-desc (in this case looking in
the editor we see 0013: I26^|"ADEV"|MVI.1) before putting that
location into the "source code" for this I-desc and then compiling the
class again.

-------------------------- With the Properties in the %MV.Adaptor class
Property TimeSpanCode As %String(MVITYPERTN = "I26^|""ADEV""|MVI.1",
MVPROJECTED = 0, MVTYPE = "I") [ Calculated, SqlComputeCode = {
Set {TimeSpanCode}=##class(O.TimeSpan).calcTimeSpanCode({%%ID},{%RECORD})},
SqlComputed ];

-------------------------- In XData MVAdditionalDictItems
<DictItem Name="TimeSpanCode">
<Attr>I</Attr>
<Attr>FIELD(@ID,'*',2)
</Attr>
<Attr>.</Attr>
<Attr>TimeSpanCode</Attr>
<Attr>40L</Attr>
<Attr>S</Attr>
</DictItem>

-------------------------- With the class methods
ClassMethod calcTimeSpanCode(
ItemID As %String,
Item As %String) As %Integer [ Language = cache ]
{
Set $mvv(16)=ItemID,$mvv(17)=Item Quit $$I26^|"ADEV"|MVI.1
}

Previously we had it coded as below, but then it did not work in SQL
or Cache' classes. Hopefully we can code them more like below in the
future (crossing fingers).

//Property TimeSpanCode As %String(MVITYPE = "FIELD(@ID,'*',2)",
MVITYPERTN = "", MVTYPE = "I", MVWIDTH = 20) [ Calculated ];

Obviously I'm hoping this approach to sourcing I-descs will be fixed
(I had indicated to folks that I was pretty sure this would be fixed
in 2008.2, but, alas...) but short of any future enhancement in that
regard at this point I at least need to figure out how to get the
hard-coded account name ("ADEV" in the above example) out of the
class.

If the namespace name is required (and I couldn't get it to work
without it, but only gave it one shot a few months back, so maybe this
is easy enough), is there a way it could be read from a file or
handled across all classes as a constant, even if that constant needs
to be changed for each account/namespace?

Thanks for any suggestions. --dawn

--
Dawn M. Wolthuis

Take and give some delight today

dawn

unread,
May 28, 2008, 12:32:35 PM5/28/08
to InterSystems: MV Community
Someone suggested offline that I clarify at little, perhaps indicate
why I want to remove hard-coded namespace names from classes, so
here's the scoop --

When switching namespaces, whether from dev to test to live (or
whatever, you get the idea) or if simply importing from an account on
a server to another account on a client or vice versa, it would not be
a good coding practice, I would think, to have the account name hard-
coded in our source code. If it needs to be read from somewhere,
preferably from the environment, but perhaps from a parm file, then we
just need to know how to do that in this instance.

Make sense? Any clues? Thanks. --dawn

John Lambert

unread,
May 28, 2008, 1:03:43 PM5/28/08
to InterSy...@googlegroups.com
Dawn,

The reason that hardcoded namespaces are needed is to allow access through Q pointers.
If you remove the namespaces then try to use a method via a Q pointer in a different namespace then it will look for the object code in the wrong namespace.

John

Dawn Wolthuis

unread,
May 28, 2008, 2:10:02 PM5/28/08
to InterSy...@googlegroups.com
Thanks for the explanation, John. Even though PI and UD don't have
anything they call Q-pointers, I think I know what they are -- a voc
entry from one account to a file "in" another. I don't know if or how
those are coded or used within the class structure (which is where we
have to specify the namespace), but I'll buy that it is needed, even
if some mysteries remain.

I still need a means of not automating the configuration management,
preferable eliminating the hard-coded namespace from source code,
otherwise isolating it to a single class somehow. The same code
should run in any namespace, right? Are there other ares where this
is required? (I have one other instance where we have it hard-coded,
but I'm not certain it is required, need to test that yet).

I'm hoping that you can generate at least that part of the code in the
future based on our "spec" in a persistent class (hopefully all of the
"I-desc hand-coded code that looks more like generated code that is
junking up our source metadata" can be generated in the future, but
that isn't the problem I'm addressing here). Then you would have what
you need somewhere in the code, but not in any code we are
maintaining, so this issue of hard-coded accounts in the code would go
away.

Is it possible to put a variable in place of the "ADEV" or do
something to make this transition from one namespace to another not
require changing source code or isolate such changes to a single
class? Thanks a bunch! --dawn

Ed Clark

unread,
May 28, 2008, 2:08:29 PM5/28/08
to InterSy...@googlegroups.com
I think you can make this a lot simpler, but I might be missing something.
This works for me:

Class O.TimeSpan Extends (%Persistent, %MV.Adaptor)
{
Property TimeSpanCode As %String(
MVITYPE = """O.TimeSpan""->calcTimeSpanCode(@ID)",
MVPROJECTED = 1,

MVTYPE = "I")
[ Calculated, SqlComputeCode = {

Set {TimeSpanCode}=##class(O.TimeSpan).calcTimeSpanCode({%%ID})
}, SqlComputed ];
ClassMethod calcTimeSpanCode(ItemID As %String) As %Integer [ Language =
mvbasic ] {
RETURN FIELD(ItemID,'*',2)
}
}

You don't need to have both a property and an XDATA block for TimeSpanCode.
The only awkwardness here is that you need to define the same method call in
both the MVITYPE parameter and in the SqlComputeCode, one in mvbasic and one
in COS. The reason for the duplication is that the I-type needs to have some
code to compile and store on it's own, because CMQL doesn't use the class at
all to get the data (often, there may not be a class at all). But it's not
so bad because both are just calling a method.

There's no need to have the MVITYPERTN in the class at all, anywhere. It
only needs to be in the DICT i-type entry, and gets put there by the i-type
compiler (which runs automatically on uncompiled i-types when needed). It's
subject to arbitrary change whenever the itype changes, so you don't want it
in the class at all.

The problem here (based on John's previous post) is that the Itype is
calling a class method. If you use the Itype through a q-pointer from
another account, you need to make sure the class is mapped into that
account. The alternative is duplicating the code from calcTimeSpanCode into
the MVITYPE parameter instead of having MVITYPE call the method directly.
But it's probably easier to maintain package mappings than to maintain
redundant code (or more redundancy then necessary, since we already have
that redundancy between MVITYPE and SqlComputeCode.)

Dawn Wolthuis

unread,
May 28, 2008, 2:29:59 PM5/28/08
to InterSy...@googlegroups.com
I'll give that a try, Ed. This both simplifies it and gets rid of the
hard-coded namespace, it appears. We need the I-desc in the class bz
we are using them with sql and with a zen object data model. Since we
are sourcing all metadata in classes, we need it in the class in order
to get it to the dict too. So, based on that, do you agree that we do
need to have it in the class?

I do have a question -- one of the issues on my list is that the
properties all have to be in one line of code, but you have split it
out into separate lines, so I'm excited about that too. When I break
it into multiple lines, it compiles, but is placed back onto a single
line. Is there a setting you have that permits these multi-line
properties? (I know it works after "SqlComputeCode = {" to go to
another line).

Thanks. --dawn

Ed Clark

unread,
May 28, 2008, 3:12:22 PM5/28/08
to InterSy...@googlegroups.com
No question that you need to have properties for the i-types in your class.
But you only need the property, not an xdata block as well, and the property
probably shouldn't have the MVITYPERTN parameter defined at all. The
dictionary entry for the i-type DOES need the reference to the routine that
would be in MVITYPERTN, with the namespace prefix. CMQL is doing a simple
execution of that routine, and needs to know the namespace to work through a
q-pointer, unless you've done routine mapping, which most "traditional"
multivalue users will probably never touch. So the routine name and
namespace need to be in the dict, but you don't need to have them in the
class.

Cache doesn't store classes as text. They are in globals with lots nodes and
subnodes. This isn't exact (not my area--ultracrepidarian warning), but
there's probably a node for the class, and under that a node for each
property, and under those a node for each parameter, and a node for each
modifier and so forth. The format is designed for efficiency in the class
compiler, and also for runtime speed. Studio rebuilds the text every time it
displays the class, but doesn't have any info on where you might have had
line breaks. At this point, it looks like you're free to put in line
breaks, but most of them aren't stored, so will be lost when the class is
redisplayed. The places where line breaks are remembered are probably inside
of code blocks--anything between {braces} is probably stored literally. I
don't know if that's something identified for enhancement or not. Studio
used to reorder all your properties in alphabetical order, but now remembers
the order.

Dawn Wolthuis

unread,
May 28, 2008, 3:38:53 PM5/28/08
to InterSy...@googlegroups.com
On Wed, May 28, 2008 at 2:12 PM, Ed Clark <ecl...@intersystems.com> wrote:
>
> No question that you need to have properties for the i-types in your class.
> But you only need the property, not an xdata block as well, and the property
> probably shouldn't have the MVITYPERTN parameter defined at all. The
> dictionary entry for the i-type DOES need the reference to the routine that
> would be in MVITYPERTN, with the namespace prefix.

Ah, OK, I misunderstood, thanks.

> CMQL is doing a simple
> execution of that routine, and needs to know the namespace to work through a
> q-pointer,

If we do not use q-pointers, can we get away without this somehow?

> unless you've done routine mapping, which most "traditional"
> multivalue users will probably never touch. So the routine name and
> namespace need to be in the dict, but you don't need to have them in the
> class.

If we keep those out of the class, will everything work until the
point we create a q-pointer?

> Cache doesn't store classes as text. They are in globals with lots nodes and
> subnodes. This isn't exact (not my area--ultracrepidarian warning), but
> there's probably a node for the class, and under that a node for each
> property, and under those a node for each parameter, and a node for each
> modifier and so forth. The format is designed for efficiency in the class
> compiler, and also for runtime speed. Studio rebuilds the text every time it
> displays the class, but doesn't have any info on where you might have had
> line breaks. At this point, it looks like you're free to put in line
> breaks, but most of them aren't stored, so will be lost when the class is
> redisplayed.

Yup, that is what happens. One of the first impressions that a new
developer (and even an old one) has in this environment is that the
classes are way too hard to read, with the long lines (sometimes a
single property line is the length of a typical java class) being a
good part of that. This is one of those things that can suck some joy
out of coding for a subset of developers, just FYI. I was pretty much
told that my request for an enhancement in this area would be closed
without putting it in any queue whatsoever (if I understood
correctly).

> The places where line breaks are remembered are probably inside
> of code blocks--anything between {braces} is probably stored literally. I
> don't know if that's something identified for enhancement or not.

I don't think so. My WRC request was closed and not passed as a
prodlog. I considered submitting a follow-up asking that studio have
a setting to show properties, splitting out indented lines at commans,
but decided that there are folks who think my requests of this nature
to be trivial and there were bigger fish to fry. But if you guys
would like to lobby a little on this, I sure wouldn't mind. It seems
like a big bang for the buck enhancement which can be isolated to the
display of the code, rather than any change to the compiler, for
example.

> Studio
> used to reorder all your properties in alphabetical order, but now remembers
> the order.

Thank goodness! Thanks. --dawn

Ed Clark

unread,
May 28, 2008, 4:06:55 PM5/28/08
to InterSy...@googlegroups.com

> -----Original Message-----
> From: InterSy...@googlegroups.com
> [mailto:InterSy...@googlegroups.com] On Behalf Of Dawn Wolthuis
> Sent: Wednesday, May 28, 2008 3:39 PM
> To: InterSy...@googlegroups.com
> Subject: [InterSystems-MV] Re: Removing account-specific code
> for I-descs
>
>
>

> On Wed, May 28, 2008 at 2:12 PM, Ed Clark
> <ecl...@intersystems.com> wrote:
> > CMQL is doing a simple
> > execution of that routine, and needs to know the namespace to work
> > through a q-pointer,
>
> If we do not use q-pointers, can we get away without this somehow?

It's not something you need to worry about. When the I-type gets compiled
(either explicitly with ICOMP or implicitly the first time it's used), it
puts the namespace-qualified routine name (the MVITYPERTN) into attribute 13
of the dictionary. So CMQL will run. You don't need the MVITYPERTN in the
class, and in fact you want it NOT to be there, because when you change the
itype in the class, and compile the class overwriting the dict entry, you
want there to be nothing in attribute 13 so that the i-type will recompile
on the next use.

Next year at Devcon, ambush the studio developers :)

dawn

unread,
Jun 25, 2008, 3:07:24 PM6/25/08
to InterSystems: MV Community
Having been delighted to read this earlier, thinking that there was a
way to code I-descs where we would not need to hard-code the account
name when specifying I-descs, I passed this info on to others and put
it aside for the "when ready to remove all hard-coded account names
from the code" effort. No one was making use of this information,
however (myself included), and we have continued to specify I-descs
the way we did before (except now it automatically compiles them,
which is much appreciated!)

It's time to get rid of those hard-coded account names in the code
and, well, shucks. I think what you are telling me is rather bad
news. See if this is accurate:

In order to take our virtual fields, specified sortof with a typical
U2-like, Prime-like spec, but expanded and complicated a tad so that
instead of editing the dict like this:

---------------------------Example dictionary code for a simple, quick
and dirty, virtual field
0001: I
0002: SignUpSnuppies + InviteSnuppies + RegistrarSnuppies +
SupportSnuppies
0003: .
0004: Total
0005: 5R
0006: S

we put the following code (looking a bit like generated code, but we
have to hand-code it) in our classes for one such field, requiring a
two-pass approach so we can find the correct number to put, such as I8
below:

--------------------------Current approach
below-------------------------

Property TotalSnuppies As %String(MVITYPERTN = "I8^|""ADEV""|MVI.1",
MVPROJECTED = 0, MVTYPE = "I") [ Calculated, SqlComputeCode = {
Set {TotalSnuppies}=##class(S.Snuppies).calcTotalSnuppies({%%ID},
{%RECORD})}, SqlComputed ];

XData MVAdditionalDictItems
{
<DictItems>
<DictItem Name="TotalSnuppies">
<Attr>I</Attr>
<Attr>SignUpSnuppies + InviteSnuppies + RegistrarSnuppies +
SupportSnuppies
</Attr>
<Attr>.</Attr>
<Attr>Total</Attr>
<Attr>5R</Attr>
<Attr>S</Attr>
</DictItem>
</DictItems>
}

ClassMethod calcTotalSnuppies(
ItemID As %String,
Item As %String) As %Integer [ Language = cache ]
{
Set $mvv(16)=ItemID,$mvv(17)=Item Quit $$I8^|"ADEV"|MVI.1
}


------------------------Suggested approach--------------------

OK, so I want to get rid of those hard-coded account names and now
that I'm diving into your example again, I see that you are not
actually specifying the I-desc using the same virtual field language
-- we would actually have to convert every I-desc from the language of
an MV dictionary to a ClassMethod written in mvbasic and passed the
right parameters from the record and all. Ugh - say it ain't so :-(

There is a language for MV virtual fields and that is the langauge we
prefer to use. If it is not feasible to use that language, then
should we write stored procedures instead of converting these to
something that isn't specified using the language we want AND runs
more slowly than a stored procedure (getting the worst of both worlds,
it seems). If we did a stored procedure, then we would still have to
do the XML for the I-desc so that the DICT had the entry.

I really, really, really do not want to switch from coding real I-
descs to coding them in mvbasic.

So, my goal here is to be able to specify an I-desc using the I-desc
specification language (which is the reason for using the XML above, I
see, and not projecting the Property) AND to eliminate the hard-coded
account name from within the specification of the virtual field.

Is that possible? Thanks in advance for caring (really!) --dawn
> > > We are currently hand-coding ourI-descsin three parts within a
> > > Take and give some delight today- Hide quoted text -
>
> - Show quoted text -

Ed Clark

unread,
Jun 25, 2008, 5:45:08 PM6/25/08
to InterSy...@googlegroups.com
I can't think of a better way than what I described. Write your code in a
class method, and then you can reuse that method everywhere: in sql, in
object coding, and from an i-type in cmql. The method is part of the class,
so it's visible from everywhere the class is.

If you want to keep close to what you're already doing, you could refine
your current process. Instead of:


> Property TotalSnuppies As %String(MVITYPERTN =
> "I8^|""ADEV""|MVI.1", MVPROJECTED = 0, MVTYPE = "I") [
> Calculated, SqlComputeCode = {
> Set {TotalSnuppies}=##class(S.Snuppies).calcTotalSnuppies({%%ID},
> {%RECORD})}, SqlComputed ];
>
> XData MVAdditionalDictItems
> {
> <DictItems>
> <DictItem Name="TotalSnuppies">
> <Attr>I</Attr>
> <Attr>SignUpSnuppies + InviteSnuppies + RegistrarSnuppies +
> SupportSnuppies </Attr> <Attr>.</Attr> <Attr>Total</Attr>
> <Attr>5R</Attr> <Attr>S</Attr> </DictItem> </DictItems> }
>
> ClassMethod calcTotalSnuppies(
> ItemID As %String,
> Item As %String) As %Integer [ Language = cache ]
> {
> Set $mvv(16)=ItemID,$mvv(17)=Item Quit $$I8^|"ADEV"|MVI.1
> }

You could use:

Property TotalSnuppies As %String(MVHEADING = "Total",
MVITYPE = "SignUpSnuppies + InviteSnuppies + RegistrarSnuppies +
SupportSnuppies",
MVJUSTIFICATION = "R",
MVTYPE = "I",
MVWIDTH = 5,
MVPROJECTED=1 )
[ Calculated,
SqlComputeCode = {
Set $mvv(16)={%%ID},$mvv(17)={%RECORD}
Set {TotalSnuppies}=$$I8^|"ADEV"|MVI.1
},
SqlComputed
];

MVITYPERTN doesn't need to be specified--it gets put into the dictionary
item when it's compiled. You don't need the xml data block--there are
parameters for all the parts of the i-type, and since you aren't specifying
MVITYPERTN anymore, you can project the i-type now. You don't need a
separate method to do the sql computation because you are just calling the
method compiled from the itype.
If you always run the application from a single account (no q-pointers) then
you don't need the namespace in the routine, so:
Set {TotalSnuppies}=$$I8^|"ADEV"|MVI.1
Can be
Set {TotalSnuppies}=$$I8^MVI.1
I think that should work, though you still need to compile the i-types, then
write the routine name back into the class. You could actually work around
that--continue to have the sqlcompute call a class method that's written in
mvbasic and uses the ITYPE function. But that would be more runtime overhead
for the sql compute.


There is no "I-desc specification language", at least not on cache. On
unidata and universe, the code you use in an i-descriptor is distinct from
mvbasic, and there are functions you can use in a basic program that don't
work in an i-type (very frustrating), and things that go into an i-type that
don't work in mvbasic. On Cache, what you put into an i-descriptor is
mvbasic, just with a compressed syntax so that it can go on one line. When
you compile an i-type it gets converted into normal mvbasic code (though in
an MVI type routine instead of the MVB routines that you are normally used
to working with). For example:

1. create a new account and logto it (this is to simplify finding the
compiled code later)
2. enter an i-type into DICT VOC that looks like:
TEST1
0001 I
0002 FIELD(@RECORD<1>,'-',1,3); INDEX(@RECORD<2>,'*',1); @2 * "20";
@1:':':@3
3. Compile the i-type with ICOMP, or use it in a list to get it compiled.
4. Look at the i-type attribute 13. It looks something like:
0013 I1^|"TEST"|MVI.1
5. Attribute 13 in my example tells us that the expanded code for the i-type
went into a routine named MVI.1.MVI as function I1. Open studio and open
MVI.1.MVI (or whatever yours says. Note that because this is an MVI routine
instead of an MVB, it isn't in a file--it's right at the top level of
studio's file view). It looks like:
$OPTIONS ITYPE VEC.MATH
DIM %MVTOTALD(),%MVTOTALI(),%MVTOTALE()
FUNCTION I1()
MV$ITYPE$1=FIELD(@RECORD<1>,'-',1,3)
MV$ITYPE$2=INDEX(@RECORD<2>,'*',1)
MV$ITYPE$3=MV$ITYPE$2 * "20"
RETURN MV$ITYPE$1:':':MV$ITYPE$3
END
Function I1() is there. It's normal mvbasic. When you use the i-type, it's
running this routine.
If you wanted, you could run this routine too, using the cos DO command:
DO I1^MVI.1
If you are running it from another account, you need to include the
namespace:
DO I1^|"TEST"|MVI.1
Which is exactly what's in the i-type attribute 13. The namespace name needs
to be in there so that the i-type will work if called through a q-pointer
from another account.

Now, enter another i-type that references the first one. Something like:
0001 I
0002 '<':TEST1:'>'
My MVI.1.MVI now looks like:

$OPTIONS ITYPE VEC.MATH
DIM %MVTOTALD(),%MVTOTALI(),%MVTOTALE()
FUNCTION I1()
MV$ITYPE$1=FIELD(@RECORD<1>,'-',1,3)
MV$ITYPE$2=INDEX(@RECORD<2>,'*',1)
MV$ITYPE$3=MV$ITYPE$2 * "20"
RETURN MV$ITYPE$1:':':MV$ITYPE$3
END
FUNCTION I2() ; RETURN '<':$FUNCTION('I1^|"TEST"|MVI.1'):'>' ; END

Function I2 will be run when i-type TEST2 gets used. The $FUNCTION function
is a function that allows you to call a cos routine directly from mvbasic.
Notice that to get the value of TEST1, function I1 is being called using
it's full namespace qualified name. If you were coding methods in a class,
that qualification wouldn't need to be there because you would know that the
class was visible.

Dawn Wolthuis

unread,
Jun 25, 2008, 6:15:35 PM6/25/08
to InterSy...@googlegroups.com
I'm OK if it isn't a better way -- the two narrowed-down requirements
specifically are:

1) Must be able to specify virtual fields using the language used to
specify virtual fields in the PI family of PICK products (maybe in
others too, I simply don't know them as well). We have been able to
do this successfully, even if somewhat tediously, thereby
single-sourcing derived data for our code -- this derived data can
then be used in Zen pages, SQL queries, and MV selects (and lists at
the colon prompt for dev purposes). That is very cool -- super!
[BTW, I am hopeful that the performance on these is not poorer than
performance in other MV products, but a little bit cautious so I hope
to keep these from writing too many I-desc until we have a better
understanding of that.]

however...

2) Must NOT have to hard-code the account name in our source code. I
would think this requirement to be obvious, but if, for some reason,
we cannot meet requirement 1 without hard-coding the account name,
then at the very least we must be able to (alternate requirement)
specify it one time for the entire account and use a variable (a
"constant variable") in our source code.

How can we accomplish 2, either the way one would like to or with a
parameter (in a file or a class, whereever) that can be specified one
time and not in every virtual field?

Thanks. --dawn

Ed Clark

unread,
Jun 26, 2008, 10:10:53 AM6/26/08
to InterSy...@googlegroups.com
I'm not understanding why you want to "specify virtual fields using the
language used to specify virtual fields in the PI family of PICK". That
"language" is terse and difficult to maintain (for anything that requires
multiple statements you need to manually keep count to use the @n result
variables) and isn't anything special--it converts trivially to normal
mvbasic. It doesn't lend itself to reuse. On the other hand, a class method
can be commented and reused as part of your core business logic. Even the
virtual field "language" provides the SUBR() function so that you can call
reusable subroutines to do anything non-trivial.

From what I understand, the core of your application is going to use SQL and
ZEN. Those get their data by using the sql compute method defined for the
property in the class. The way you are doing it now, ZEN calls a sql compute
method, which then calls a routine compiled from an itype. It would be
marginally more efficient, and more readable, if the sql compute method did
the actual work itself instead of calling another routine. When you use cmql
"at the colon prompt for dev purposes" the i-type will be calling a class
method, which might be marginally slower, but will be much better
documented.

The way you are doing it now--having the i-type routine do the work--traps
you in a 3-step method. Every time you change the i-type code (or when you
first create it) you need to compile the class, compile the i-type, and then
copy the routine name into the class and compile again. The class compiler
can't compile the i-type for you, because itypes may depend on other itypes
in other files or accounts, but even if it did you would still have to go
back and put the itype routine into the method. You could probably use a
generated method to fill in the routine name, but you would still have to
compile the class twice. You could use a program to do the compilations
using the %SYSTEM.OBJ class methods, but it seeems like a lot of work for a
workaround.

As I mentioned before (below) you don't need to put the namespace name as
part of the routine:
Quit $$I8^|"ADEV"|MVI.1
Could be simply
Quit $$I8^MVI.1
Provided that your application is running all from one namespace, or that
you're never going to call the method from a different namespace. I suspect
that's probably the case, so you don't need the namespace qualifier there.

Dawn Wolthuis

unread,
Jun 26, 2008, 11:11:16 AM6/26/08
to InterSy...@googlegroups.com
On Thu, Jun 26, 2008 at 9:10 AM, Ed Clark <ecl...@intersystems.com> wrote:

I'm not understanding why you want to "specify virtual fields using the
language used to specify virtual fields in the PI family of PICK".
 
The answer to that question is very similar to the answer to the question of why we are using PICK in the first place, or to why we are not switch to COS for this or that.  I realize we will not be able to write most of the software such that it would handily migrate to another MV platform, but for various business reasons, I would like to ensure that it would be a relative piece of cake to migrate the metadata to another MV platform, should that need arise.  A field specified here should have a dict entry pretty much identical to a field specified in U2.  Additionally, we have to add plenty to our developer skillsets in writing AJAX software with Cache', so I would like to retain the ability to specify an I-desc as expected.  These are not show-stopper requirements, but I'm pretty protective of metadata in this regard, compared to the rest of the code.
 
That
"language" is terse and difficult to maintain (for anything that requires
multiple statements you need to manually keep count to use the @n result
variables) and isn't anything special--it converts trivially to normal
mvbasic.
 
Yup.  I'll re-read and reflect on the rest later due to a time constraint right now.  Thanks.  --dawn

Jason Warner

unread,
Nov 24, 2008, 4:37:30 PM11/24/08
to InterSy...@googlegroups.com
Ed,

I'm coming at this a bit after the discussion and I'm running into some problems that I was wondering if you could shed some light on. I have the following dictionary in one of my MV classes:


Property BidderCentralDealerNumber As %String(COLLATION = "MVR",
   MVITYPE = """MVFILE.CAR2EFILE""->calcBidderCentralDealerNumber(@ID, @record)",
   MVNAME = "BidderCentralDealerNumber",
   MVPROJECTED = 1,
   MVTYPE = "I") [ Calculated, SqlComputeCode = {Set {BidderCentralDealerNumber}=##class(MVFILE.CAR2EFILE).calcBidderCentralDealerNumber({%%ID},{%RECORD})},
SqlComputed 
];

I have the associated function in my class as follows:

ClassMethod calcBidderCentralDealerNumber(ItemID As %String, Item As %String) As %String [Language = mvbasic]
{
retval = ""

call GET.VM.1(retval, Item)

return retval
}

The issue that I'm having is that when the itype is compiled, the "-" in the "->" is being turned into "@record<32>" and the compile fails. I tried just putting "-" in the MVITYPE parameter and the compiled dictionary became "RETURN @record<32>" for some reason. Is there a better way to achieve namespace independence?

Our issue is that we are using subversion for source control and each developer has their own namespace which is also different from the namespace of the live servers. We are trying to deploy dictionary items by compiling the classes that they pertain to so that we can check our file definitions into source control. Unfortunately, I am running into all sorts of headaches when trying to use these dictionaries in namespaces other than my own.

Jason

Ed Clark

unread,
Nov 24, 2008, 5:35:15 PM11/24/08
to InterSy...@googlegroups.com
I haven't been able to duplicate what you're seeing. I don't see the -> getting changed to @record<32> in the i-type.

Jason Warner

unread,
Nov 24, 2008, 5:54:21 PM11/24/08
to InterSy...@googlegroups.com
That is a little disconcerting. I am on Cache for Windows (x86-32) 2008.2 (Build 526_0_7489) Tue Nov 11 2008 13:09:40 EST. I am running it under the "REALITY" emulation. When I compile my class, I get this in the dictionary:

001 I
002 "MVFILE.CAR2EFILE"->calcBidderCentralDealerNumber(@ID, @record)
003
004
005 10L
006 S
007
008
009
010
011
012
013

When I list the file with that dictionary, I get the following error message:

JASON:list car.file BidderCentralDealerNumber
 
Error in BidderCentralDealerNumber: Undimensioned Array  TEXT: FUNCTION I16() ;RETURN "MVFILE.CAR2EFILE"@RECORD<32>>calcBidderCentralDealerNumber(@ID, @record) ; END
[178] The EVAL expression "BidderCentralDealerNumber failed to compile.

The replacement doesn't take place until I actually try to use the dictionary item. If I replace line 2 with a "-", I get a compiled routine that looks like:

FUNCTION I15() ; RETURN @RECORD<32> ; END

I'm a little concerned that I'm not seeing the same results as you.

Jason

Ed Clark

unread,
Nov 24, 2008, 6:26:02 PM11/24/08
to InterSy...@googlegroups.com
Just grasping at straws here... is it possible that you have a dictionary item in the file who's id is a minus sign?

Jason Warner

unread,
Nov 24, 2008, 7:01:02 PM11/24/08
to InterSy...@googlegroups.com
Ed,

You are absolutely correct. I had a dictionary named "-". Thanks for looking at this one for me.

Jason

Jim Idle

unread,
Nov 24, 2008, 7:50:22 PM11/24/08
to InterSy...@googlegroups.com
On Mon, 2008-11-24 at 15:54 -0700, Jason Warner wrote:
That is a little disconcerting. I am on Cache for Windows (x86-32) 2008.2 (Build 526_0_7489) Tue Nov 11 2008 13:09:40 EST. I am running it under the "REALITY" emulation. When I compile my class, I get this in the dictionary:

Jason,

You are being misled a little by the code that is generated to evaluate the Itype. I16() is the name of a function that has been generated to evaluate your itype at runtime. Looks like there is a bug in the code generator/compiler for itypes that is not completely encoding your reference:

"MVFILE.CAR2EFILE"->calcBidderCentralDealerNumber

It may in fact be a parsing error by the looks of it. I am sure it is easy enough to fix, but are you sure that this is in fact the correct formulation for what you are trying to achieve?

Jim

Jason Warner

unread,
Nov 25, 2008, 12:06:15 PM11/25/08
to InterSy...@googlegroups.com
Jim,

I'll be the first to admit that I'm not sure about the best way to go about this. Here is what I'm trying to accomplish and maybe there is a better way than what I'm trying to do.

We have set up a development environment where each developer has their own namespace and a sandbox of code checked out from source control. This works for us really well and we want to keep that setup. We are trying to develop a strategy for deploying dictionaries from the class because that seems like it will be the easiest way to deploy to multiple servers and namespaces.

The problem is that the code that is generated by PROTOCLASS is namespace dependent. I just found out last night that the storage code had put my namespace in the references so queries in another namespace was returning result sets from my namespace. We are trying to achieve namespace independence specifically for itypes. This thread had some good ideas and I've implemented many of the ideas that Ed suggested here for one test class and it seems to work ok.

I guess the big question is, is there a better way to deploy classes with itypes to different namespaces and have the generated code work? The most frustrating part is that the MVI.1 file that is generated for itypes is entirely dependent on the order and number of itypes previously generated.

Jason

Jim Idle

unread,
Nov 25, 2008, 12:23:41 PM11/25/08
to InterSy...@googlegroups.com
On Tue, 2008-11-25 at 10:06 -0700, Jason Warner wrote:
Jim,

I'll be the first to admit that I'm not sure about the best way to go about this. Here is what I'm trying to accomplish and maybe there is a better way than what I'm trying to do.

I think that the namespace dependent stuff has changed since I was there to be honest, so I may not be in the best position to advise you. Given that you found the "-" dictionary item and fixed that, then perhaps you are already on the right track. So long as you don't think you are creating yourself a future maintenance headache, then stick with what seems to work unless any of the current MV team tell you any differently.


We have set up a development environment where each developer has their own namespace and a sandbox of code checked out from source control. This works for us really well and we want to keep that setup. We are trying to develop a strategy for deploying dictionaries from the class because that seems like it will be the easiest way to deploy to multiple servers and namespaces.

The problem is that the code that is generated by PROTOCLASS is namespace dependent. I just found out last night that the storage code had put my namespace in the references so queries in another namespace was returning result sets from my namespace. We are trying to achieve namespace independence specifically for itypes. This thread had some good ideas and I've implemented many of the ideas that Ed suggested here for one test class and it seems to work ok.

I guess the big question is, is there a better way to deploy classes with itypes to different namespaces and have the generated code work? The most frustrating part is that the MVI.1 file that is generated for itypes is entirely dependent on the order and number of itypes previously generated.

Yes - this creates runtime efficiencies but does have this as a side-effect. To be honest, I think that the whole I-Type thing is asking for trouble anyway as once you start running queries on such things then the dictionary can do anything and there is no reliable way to optimize the query and so on. While using BASIC in dictionaries yields the same problems as the engine cannot know what the heck you do in the code, you might find it easier to deploy via global cataloging, which should work in all name spaces. I would avoid I-types and their myriad inconsistencies.

Jim

Jason Warner

unread,
Nov 25, 2008, 12:30:34 PM11/25/08
to InterSy...@googlegroups.com
Thanks for you advice Jim. I would like to clean up our dictionaries and get away from some of the interesting stuff I see that looks like it is trying to provide information that a simple SQL join can take care of now. I think once we get up and going, I will make sure to bring up this discussion with our group.

Jason

Dawn Wolthuis

unread,
Nov 25, 2008, 12:44:34 PM11/25/08
to InterSy...@googlegroups.com
Jason -- Our I-descs are now namespace-independent using the approach
that I put out here in a document

http://groups.google.com/group/InterSystems-MV/web/example-i-desc-for-sql-zen-mv

It is not pretty, but it works for us, still hopeful that in the
future making I-descs will be more developer-friendly.

We are using the cache flavor, in case that is relevant. We use
multiple namespaces for different purposes, but not for different
developers, and it works fine after we do this initial setup of the
I-desc. Is there something about this approach that does not work in
your situation? --dawn

Ed Clark

unread,
Nov 25, 2008, 12:59:42 PM11/25/08
to InterSy...@googlegroups.com
maybe you could use routine mapping to map the MVI.xxx routines (MVI.1 overflows to MVI.2 when it gets large) to a single master copy. Then the original class as generated by PROTOCLASS would be usable in all namespaces.

Dawn Wolthuis

unread,
Nov 25, 2008, 1:07:36 PM11/25/08
to InterSy...@googlegroups.com
What is PROTOCLASS? I searched in the doc, in %sys, and the voc.
Thanks. --dawn

Jason Warner

unread,
Nov 25, 2008, 1:21:04 PM11/25/08
to InterSy...@googlegroups.com
Dawn,

I did find your document and read through it. The biggest problem that
we had was getting the MVI.1.mvi routine into source control.
Fortunately, we don't have the same constraints that you do and I am
allowed to try all sorts of different options. The option that Ed
outlined seems to work ok for us. The key seems to be leaving out or
removing the MVITYPERN parameter. We are just running up against a
really ugly dictionary for a couple of files. It looks like previous
developers were into creating dictionaries for the sake of saving a
couple of key strokes (a dictionary named "-" is a good case in point).

Our entire deployment procedure is based on our source control. To
deploy code, we check it out from the repository and then import it and
compile it. This has actually worked very well in Cache and that is the
driving force behind a lot of this discussion. We want an easy way to
deploy dictionaries to our various servers and namespaces.
Unfortunately, like you encountered, moving my code from my namespace to
our staging or other developer namespaces has caused headaches.


Jason

Ed Clark

unread,
Nov 25, 2008, 1:22:32 PM11/25/08
to InterSy...@googlegroups.com
CREATE.CLASS is renamed to PROTOCLASS in recent builds.

Jason Warner

unread,
Nov 25, 2008, 1:23:14 PM11/25/08
to InterSy...@googlegroups.com
PROTOCLASS was originally named CREATE.CLASS. It allows you to take existing dictionary items and create a class from them. We use it extensively because we already have existing dictionaries from our jBase application that we want to expose to SQL/.Net/JDBC/etc.

Jason Warner

unread,
Nov 25, 2008, 1:29:51 PM11/25/08
to InterSy...@googlegroups.com
Ed,

We discussed this before exploring the path I'm going down. The developers here felt that it would create a network dependence. I didn't know enough about routine mapping to argue one way or the other. If we set up routine mapping, does it create a network dependence for remote machines? If not, can I create the routine mapping, remove the MVITYPERTN routine, put our code in the MVITYPE and then have it build the new itypes in the mapped routine?

Jason

Ed Clark

unread,
Nov 25, 2008, 1:54:44 PM11/25/08
to InterSy...@googlegroups.com
If you are using the class as the "master" source for your dictionaries, then the class should not contain the MVITYPERTN parameter. Then, when you compile the class in a namespace, it will write out the new/updated dictionary entries without the routine reference (in attribute 13) and a routine/reference will be created later when the i-type is used (or if you ICOMP first). Using this method, you don't need to worry at all about the MVI.xxxx routine. You would then only run into an issue if you copy a dictionary entry with a routine reference in it from one namespace to another--but you wouldn't do that, because the class is your source.

James Glaser

unread,
Dec 1, 2008, 10:01:37 AM12/1/08
to InterSy...@googlegroups.com

Group,

 

I’ve recently done some playing around trying o hook SSRS (SQL Server Reporting Services) up to my dev Cache server. Unfortunately SSRS is pretty picky about what sort of datasource it would hook up too and even though I’ve added Cache as an ODBC datasource I didn’t have any luck getting a connection.

 

Has anyone ever successfully done this, and what was the trick to making the connection.

 

Thanks,

James

 

Michael Cohen

unread,
Dec 1, 2008, 10:10:42 AM12/1/08
to InterSy...@googlegroups.com
One approach to figuring out what is failing is to turn on ODBC logging in the Caché DSN, try to connect, and examine the log to see what SSRS is asking.


From: InterSy...@googlegroups.com [mailto:InterSy...@googlegroups.com] On Behalf Of James Glaser
Sent: Monday, December 01, 2008 10:02 AM
To: InterSy...@googlegroups.com
Subject: [InterSystems-MV] Cache + SSRS

James Glaser

unread,
Dec 1, 2008, 12:12:46 PM12/1/08
to InterSy...@googlegroups.com

The ODBC connection works fine, it just does not appear when trying to select it when creating a datasource in SSRS from Visual Studio 2005.

<BR

Reply all
Reply to author
Forward
0 new messages