Robert Cemper · Nov 13, 2017 go to post

Thank You Alex!
I knew it's a can of worms and spread over documentation like feeding pigeons.
And honestly I was pretty sure not catch them all and hesitated quite some time to touch it..   
yes  Great contribution.

Robert Cemper · Nov 13, 2017 go to post

%On... callbacks are served and integrated into he OBJECT world and typically don't care about any Trigger.

Triggers live in the SQL TABLE world of your class, with a hand full off %-variables unknown at the Object side
and without an actual instance of object but directly writing to Globals.

Take a look to the generated .INT code of your class and see what you have at hands.
I always found it quite ambitious to attempt a common code of both sides when directly changing Objects or Tables.

Summary:
- for SQL access have Triggers
- for Object access have your %On* methods

Applies only before 2014.1 as pointed out. 
But Trigger code doesn't look much better. 

Robert Cemper · Nov 10, 2017 go to post

something similar:

start ;
 open infile:"R":0 else  write "input file not found",! quit
 open outfile:"WNS":0 else  write "error creating output file",! quit 
 set $Zt="end"
 for line=1:1:5 {
  use infile read sql use 0
  if $e(sql,1,6)'="SELECT" continue
  set rs=##class(%ResultSet).%New()
  set sc=rs.Prepare(sql)
  set:sc sc=rs.Execute()
  if 'sc write "bad SQL statement",! quit
  set cols=rs.GetColumnCount()
  use outfile
;; fill in headers if required
  while rs.Next() {
   for cols=1:1:cols write rs.GetData(cols),$c(9)
   write !
  }
  write !,"###",!
  use 0
 }
 
end set $ZT=""
 close infile,outfile quit
 
!! NOT TESTED !!
Robert Cemper · Nov 10, 2017 go to post

%Save() stream doesn't mean closing.
and Rewind() is different from %Reload().

So the intention of the 3 out-commented lines is not obvious.

Opposite to your comment %Open() does a reload from disk.

Robert Cemper · Nov 8, 2017 go to post

It's available if you update an already existing object.

NOT available for a New or Cloned object

Robert Cemper · Nov 8, 2017 go to post

Do I understand it correctly?

?  Duplicates are allowed but detected.
?  Uniqueness is not forced
Robert Cemper · Nov 7, 2017 go to post

correct.

OVER(PARTITION BY

is an unknown keyword in documentation 
You may need to reformulate your query to achieve a similar result
 

Robert Cemper · Nov 6, 2017 go to post

Sorry, I don't have any Doc reference at hands.
Eventually someone of the Atelier team has.

Out of practical observation
there is a quite fundamental shift in the editing paradigm between Studio and Atelier.

Studio does Server based editing. What you change is there and during your changing session
you lock the Class, Routine, ... So you act as single owner at that time and
anyone else trying to change something gets an immediate alert.  
It's the "Highlander Principle" (according to the film"There can be only One" http://wiki.c2.com/?HighlanderPrinciple
It's based on classical LOCK logic. "Pessimistic Locking"

Atelier acts on your local copy and no one else might know unless you use some source control. !!

At compile time your copy is checked and if it was changed you get a nice Text-Diff to decide how to to proceed.
So we see "Optimistic Locking" or "Versioning".

You can easily reproduce this:
    - open some Class or Routine in Studio and do any change (e.g. Comment) but no compile or save
    - open the same with Atelier. - no alert
    - change it in Atelier. - no alert
    - save it in Atelier. NOW you get your alert from server but you keep your local copy.
    - save your copy in Studio. - you are the winner

next:
    - do some dummy change in Atelier (I found no other way)
    - save it in Atelier and you get the Text-Diff with your version and the actual server version
     but as in past you get no hint how many changes you may have missed.

personal remark:
I think at that point latest anyone should have understood the importance of source control.
to know of an intended change in advance and his history later.
 

Robert Cemper · Nov 6, 2017 go to post

having this done ~ 9 yrs back I can't resist to share my old solution (at that time for a UNIQUE on 2000  char)

- split Text into 4 sections in calculated properties e.g.
tx1 = $e(Text,1,500)
tx2 = $e(Text,501,1000) 
tx3 = $e(Text,1001,1500)
tx4 = $e(Text,1501,2000)

- index them
- then you can build an SQL  statement to have all 4 pieces identic.
you end up with a cascade of embedded SELECTS

It's not to fast but very precise.
 

Robert Cemper · Nov 5, 2017 go to post

Found it:

Exceptions to READ COMMITTED  1 of some more

If you query contains an aggregate function, the aggregate result returns the current state of the data,
regardless of the specified ISOLATION LEVEL. Therefore, inserts and updates are in progress (and may subsequently be rolled back) are included in aggregate results. Deletes that are in progress (and may subsequently be rolled back) are not included in aggregate results. This is because an aggregate operation requires access to data from many rows of a table.

see:

http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY…

Robert Cemper · Nov 5, 2017 go to post

being suspicious on any * do you see the same behavior for 

select count(AttrA) into :ans from Test.Table)
Robert Cemper · Oct 27, 2017 go to post

Hi Eduard !

Being a little bit lazy I simplified the query (for less typing)
My example: 

SELECT * FROM %Dictionary.StorageDataValueDefinition
where id [ 'Sample.Person'

And it works fine for persistent and serial classes.
My hidden assumption: there is only 1 Storage Strategy.

The nice point about:
you get also storage locations of deleted (!!) properties  
that eventually might be invisible in class definition.
 
yes yes

Robert Cemper · Oct 25, 2017 go to post

ECP always acts as Master/Slave relation.
Server 2 holds the "FROM" DB as ECP master while Server1 pulls it down as ECP client.

So from logic Server2  just can "reply" to requests from Server1.
Though I'm not sure  how a firewall in between has to be configured.  

Robert Cemper · Oct 25, 2017 go to post

Assumimg you have both DB on the same instance but different namespaces "FROM" and "TO"

You may run a loop like this

set id=""
for cnt=1:1 {
  set id=$ORDER(^|"FROM"|Data(""),1,value) quit:id=""
  set ^|"TO"|Data(id+10000000)=value)
  if cnt#100000 write cnt,?10,id,!
}

For the connections of the host you may use ECP

For a more structured global you might need to use $QUERY()

The write is just to see progress

Robert Cemper · Oct 22, 2017 go to post

YES it is !

Class %DeepSee.ResultSet has a method %CancelQuery that does the trick.

http://docs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.cls?…

http://docs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.cls?…

Chapter  Using the Result Set API tells you some more details.
http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY…

especially:

If you used %ExecuteAsynch(), periodically check to see whether the query has completed. If the query uses any plug-ins, make sure that any pending results are also complete; pending results are the results from the plug-ins, which are executed separately from the query.

To determine the status of the query, call the %GetStatus() method of your instance. Or call the %GetQueryStatus() class method of %DeepSee.ResultSet. These methods return the status of the query and also (separately) the status of any pending results; see the class documentation for details.

Optionally, to cancel a query that has not yet completed, call the %CancelQuery() class method.

A practical example is seen in ##class( %DeepSee.UI.Analyzer). onunloadHandler()

Robert Cemper · Oct 19, 2017 go to post

Changing IDKey to %Integer is not enough for Bitmap Indexing

You also have to set MINVAL =1  to prevent 0 or negative integers !

Robert Cemper · Oct 19, 2017 go to post

Hi Scott,

analyzing  ##class( EnsLib.SQL.Common).ExecuteProcedureParmArray(.....)
I think you should be able to provide a prepared Snapshot to set  the required parameters.

At least $$$sysTRACE("Using initialized SnapShot "_(tNumRS+1))  points in this direction

So your code might look like this:

Set SelectPER355MC=##class(%ListOfObjects).%New()
Set preset=##class(EnsLib.SQL.Snapshot).%New()
Set preset.MaxRowsToGet=12000
do SelectPER355MC.SetAt(preset,1)
set tSC = ..Adapter.ExecuteProcedureParmArray(.SelectPER355MC,.output,SPQuery,"oi",.parm)

I have no environment to check the approach.
So it's up to you to verify it.

Robert Cemper · Oct 18, 2017 go to post

I'm sorry.

I'm not aware of ANY documentation on required privileges by any Query.
I understand quite well your intention but especially <Protect> is hard to trap double hard inside a Class Query
With privileged app you limit the scope and get control over access parameters.