Vitaliy Serdtsev · Apr 12, 2021 go to post

See CREATE FUNCTION

  • Create your own counting function:
    CREATE FUNCTION my.GetCalcTableExtentSize(IN SchemaName SYSNAMEIN TableName SYSNAMERETURNS BIGINT(''PROCEDURE LANGUAGE OBJECTSCRIPT
    {
     quit ##class(%SQL.Manager.Catalog).GetCalcTableExtentSize(SchemaNameTableName)
    }
  • Now you can use it in queries:
    select table_schema "Schema"table_name TableName,my.GetCalcTableExtentSize(table_schema,table_nameRowCount from information_schema.tables where table_type in ('BASE TABLE','VIEW')
Vitaliy Serdtsev · Apr 7, 2021 go to post

See %Regex.Matcher

Example:

text "This is a sample blob of text",
  keywords="This,blob,text"

matcher=##class(%Regex.Matcher).%New($tr(keywords,",","|")),
  matcher.Text=text
w:matcher.Locate() "hit",!

matcher.ResetPosition()

while matcher.Locate() {write "Found ",matcher.Group," at position ",matcher.Start,!}

USER>^test
hit
Found This at position 1
Found blob at position 18
Found text at position 26

Or see $locate: Using Regular Expressions in Caché

Example:

USER>w $locate(text,$tr(keywords,",","|"),1,e,x)
1
Vitaliy Serdtsev · Apr 2, 2021 go to post

By the way, InterSystems employees have already given examples of undocumented code here, which can be found independently in %SYS:

  • https://community.intersystems.com/post/there-way-get-internal-private-property
  • https://community.intersystems.com/post/what-best-way-serialize-objectlistarrayetc-string#comment-84976
Vitaliy Serdtsev · Apr 2, 2021 go to post
If you look at a modern version of Caché or IRIS you'll find that ^%SYS.SECURITY is hidden.
What does the source code of ^%SYS.SECURITY have to do with it? The fact that it is hidden does not play any role here.

Take a look at the code %CSP.Portal.Home:%OnPreHTTP()

Vitaliy Serdtsev · Apr 2, 2021 go to post

I keep my promise (yes, it wasn't an April Fool's joke ;)

  • The first way is associated with a dummy field for the sake of being able to override the final BuildValueArray method and avoid the following error
    ERROR #5272: Can't change final 'Method' : 'BuildValueArray')
    Class rcc.IC.ItemList Extends (%Persistent%Populate) [ Final ]
    {
    Index xitmp On (ItemsP(KEYS), ItemsP(ELEMENTS));
    Property Company As %String Required ];
    Property Region As list Of %String(COLLATION "EXACT"POPSPEC ":4"VALUELIST ",US,CD,MX,EU,JP,AU,ZA") [ Required ];
    Property Items As list Of rcc.IC.serItem(POPSPEC ":4") [ Required ];
    Property ItemsP As %String(COLLATION "EXACT") [ CalculatedPrivateReadOnlyRequiredSqlComputeCode = {{*} {Items}}, SqlComputed ];
    ClassMethod ItemsPBuildValueArray(
      value,
      ByRef arrayAs %Status
    {
      ptr=0
      while $listnext(value,ptr,val){
        v=$li(val,1),
          array($li(v,1))="Subject",
          array($li(v,2))="Change",
          array($li(v,3))="Color"
      }
      q $$$OK
    }
    

    }

    select count(IDfrom rcc_IC.ItemList where FOR SOME %ELEMENT(ItemsP) (%key in ('blue','yellow') and %value='Color')

    or if need to find values in any fields

    select count(IDfrom rcc_IC.ItemList where FOR SOME %ELEMENT(ItemsP) (%key in ('blue','yellow'))

    For the sake of speed, you can store in the index not all the fields of the serial class, i.e.:

    Index xitmp On ItemsP(KEYS);
    

    ClassMethod ItemsPBuildValueArray(   value,   ByRef arrayAs %Status {   ptr=0   while $listnext(value,ptr,val){     v=$li(val,1),       array($li(v,3))="" ; only Color   }   q $$$OK }

    select count(IDfrom rcc_IC.ItemList where FOR SOME %ELEMENT(ItemsP) (%key in ('blue','yellow'))

    You can also add more dummy fields and accordingly indexes to cover all possible queries.


  • The second way involves changing the storage and creating a virtual table
    Class rcc.IC.ItemList Extends (%Persistent%Populate) [ Final ]
    {
    Index xitm On Items(ELEMENTS).Color;
    Index xitm1 On (Items(ELEMENTS).Color, Items(KEYS));
    Property Company As %String Required ];
    Property Region As list Of %String(COLLATION "EXACT"POPSPEC ":4"VALUELIST ",US,CD,MX,EU,JP,AU,ZA") [ Required ];
    Property Items As list Of rcc.IC.serItem(POPSPEC ":4"STORAGEDEFAULT "array") [ Required ];
    

    }

    select count(distinct IDfrom rcc_IC.ItemList where ItemList_Items->Items_Color in ('blue','yellow') -- index "xitm1" is used

    or even faster

    select count(IDfrom rcc_IC.ItemList where FOR SOME %ELEMENT(Items) (%Value in ('blue','yellow')) -- index "xitm" is used

    Of course, the data can be accessed from both tables, just do not forget about SetCollectionProjection GetCollectionProjection (for more information, see my article SQL index for array property elements)

Vitaliy Serdtsev · Apr 2, 2021 go to post

The %SYS namespace sources are open to study and often (but not always) serve as a coding etalon reference for application developers. This is a whole storehouse of knowledge for those who want to better understand certain mechanisms work of system classes.
If some of the commands found there are not documented, but effectively do useful work, then why can't they be used by application developers?
In addition, I do not rule out the fact that some of them are simply forgot to document.

What really needs to be hidden or potentially dangerous is already hidden in the deployed classes.

Vitaliy Serdtsev · Apr 1, 2021 go to post

Hi Robert.

I also got carried away with this question and found two more ways to use indexes for a list of serial objects, and you can explicitly specify specific fields in the query, rather than $list (%Value,3).

The speed may be not always the best, but I did the best I could. I tested on Caché (perhaps something has been improved in IRIS in this regard?)

If you're interested, I can share it.

Vitaliy Serdtsev · Apr 1, 2021 go to post

Try the following:

status=$$DeleteSession^%SYS.cspServer("wuuZ2Gwgxw")

This is not documented or supported. Use at your own risk.

But since you specified Caché 2018.1, which is no longer being developed so not may change in the future (not counting security patches).

Vitaliy Serdtsev · Apr 1, 2021 go to post

Topicstarter indicated Caché 2018.1, which is no longer being developed so not may change in the future (not counting security patches)

Vitaliy Serdtsev · Mar 31, 2021 go to post

Unfortunately, %INLIST does not use indexes (at least it was in Caché, I do not know how it is now in IRIS), which I made a note about in my article.

Vitaliy Serdtsev · Mar 31, 2021 go to post

$LISTFIND certainly does its job well, but there is a better solution.

As the data grows, the search speed will drop, since this solution does not use indexes in any way. Therefore, the best solution is to use the predicate FOR SOME %ELEMENT.

For more details with examples, see one of my articles: SQL Performance Resources (item k. Indexing of non-atomic attributes)

Vitaliy Serdtsev · Mar 31, 2021 go to post

Only customers with the corresponding paid valid service have access to the download from WRC (see in the price list the section Services Fees: Software Updates, Technical Assistance).
Those who use the Community Edition or who have expired, can not download anything from there.

Therefore, in this case, open resources, such as Github or FTP, are more preferable.

Vitaliy Serdtsev · Mar 31, 2021 go to post

Try the following:

SAMPLES>set sc=$$GetSecurityUserInfo^%SYS.SECURITY($username,.p)

SAMPLES>zwrite p
p("AccountNeverExpires")=..
p("AutheEnabled")=..
p("ChangePassword")=..
p("Comment")=..
p("CreateDateTime")=..
..
p("FullName")=..
p("Roles")=..
p("PhoneProvider")=..
p("Routine")=..
p("SuperUser")=..

Vitaliy Serdtsev · Mar 4, 2021 go to post

The interactive tutorials with exercises built into the documentation helped me a lot in my time:

Also included was a database "SAMPLES" (code and data) with examples for every taste. I often used it for performance testing.

Note: I give a link to version 2016.2, because since version 2017.1, for some reason, the developers have changed the display styles in the online documentation and removed some information. Local documentation does not have these disadvantages.

In addition, in the <cachesys>\dev\ folder there were numerous demos with source code for various programming languages and connection technologies (С++, C#, Java, PHP, Python, ODBC, JDBC, ADO.NET, ActiveX, XEP, etc.):

 

Incomplete content


\dev\tutorials\
\dev\tutorials\csp
\dev\tutorials\java
\dev\tutorials\mv
\dev\tutorials\pojo
\dev\tutorials\security
\dev\tutorials\UnitTestEx
\dev\tutorials\webservices
\dev\tutorials\zen

\dev\Cache\
\dev\Cache\callin
\dev\Cache\callout
\dev\Cache\cctrlcpp
\dev\Cache\cctrlvb
\dev\Cache\ctrldemo
\dev\Cache\HAcluster

\dev\odbc\samples\
\dev\odbc\samples\php
\dev\odbc\samples\select
\dev\odbc\samples\sqlgateway

\dev\python\
\dev\python\samples
\dev\python\samples3

\dev\cdirect
\dev\cpp\samples
\dev\dblib\samples
\dev\dotnet\samples
\dev\java\samples
\dev\mv\samples
\dev\node.js\samples
\dev\perl\samples
Vitaliy Serdtsev · Jan 12, 2021 go to post

N<=9 (59 chr)

N>9 (62 chr)

 

Code

n=1:1:j=n-1:-1:1,0:1:!?i=1:1:n-j,i-1:-1:1 i (59 chr)

n=1:1:j=n-1:-1:1,0:1:!?i=1:1:n-j,i-1:-1:1 i#10 (62 chr)

Who is shorter? ;)

Vitaliy Serdtsev · Nov 27, 2020 go to post

Examining an XML Subtree

  try{

    

$$$ThrowOnError(##class(%XML.XPATH.Document).CreateFromString("<a><b><c>some content</c></b></a>", .doc))
    
$$$ThrowOnError(doc.EvaluateExpression("/a""b", .field))
    
#dim obj As %XML.XPATH.DOMResult field.GetAt(1)
    
    
while obj.Read() {
      
if obj.HasValue {
        
write obj.Path,": ",obj.Value,!
      
}
    }
    
  }
catch(ex){
    
write "Error "ex.DisplayString(),!
  
}

Result:
b\c\: some content

Vitaliy Serdtsev · Nov 26, 2020 go to post

    set t={"id":"1","Code"2}
    write t.%ToJSON(),!
    
    do t.%Set("Code","","null")
    write t.%ToJSON()

%Set

Vitaliy Serdtsev · Nov 9, 2020 go to post

There is another way.
If you want any empty strings to always be treated as null/"" instead of "/$c(0), then there is an documented setting (within the scope of namespace), namely:

^%SYS("sql","sys","namespace",<YOUR_NAMESPACE>,"empty string")

Here is a small example:

Class dc.test Extends %Persistent
{

Property As %Integer;

Property str As %String;

/// do ##class(dc.test).Test()
ClassMethod 
Test()
{
 
do ..%KillExtent()
 
 
try{
  
  
do $system.SQL.Purge()
  
set ^%SYS("sql","sys","namespace",$namespace,"empty string")=$c(0)

  

; '' -> $c(0)
  
do ##class(%SQL.Statement).%ExecDirect(,"insert into dc.test(i,str) values(1,'')")
  
  
; null -> ""
  
do ##class(%SQL.Statement).%ExecDirect(,"insert into dc.test(i,str) values(2,null)")
  
  
do $system.SQL.Purge()
  
set ^%SYS("sql","sys","namespace",$namespace,"empty string")=""

  ; '' -> ""
  
do ##class(%SQL.Statement).%ExecDirect(,"insert into dc.test(i,str) values(11,'')")
  
  
; null -> ""
  
do ##class(%SQL.Statement).%ExecDirect(,"insert into dc.test(i,str) values(22,null)")

  

zwrite ^dc.testD
  
kill ^%SYS("sql","sys","namespace",$namespace,"empty string")
 
}
 
catch(ex){
  
write ex.DisplayString(),!
 
}
}

}

USER>do ##class(dc.test).Test()
^dc.testD=4
^dc.testD(1)=$lb("",1,$c(0))
^dc.testD(2)=$lb("",2,"")
^dc.testD(3)=$lb("",11,"")
^dc.testD(4)=$lb("",22,"")

Vitaliy Serdtsev · Nov 9, 2020 go to post

Another option just for the fun:

Include %SYS.PTools.Stats

Class dc.test Abstract ]
{

ClassMethod setValue(args...) As %Status
{
  
quit:args<2 $$$ERROR($$$DataMissing)

  

$$$convertArrayToList(args,list)
  
quit:$listlength(list)'=args $$$ERROR($$$RequiredArgumentMissing)
  
  
set $list(list,*,*)="",
         
var=##class(%Utility).FormatString(list),
         
$extract(var,1,3)=$name(%sessionData), ##; or $name(%session.Data)
         
@var=args(args)
  
quit $$$OK
}

/// do ##class(dc.test).Test()
ClassMethod 
Test()
{
  
new %sessionData

  

try{

    

do $system.OBJ.DisplayError(..setValue("key1""val1")),
          
$system.OBJ.DisplayError(..setValue("key1""key2""key3""key4""val2")),
          
$system.OBJ.DisplayError(..setValue("key1""key2", , "key4""val3")),
          
$system.OBJ.DisplayError(..setValue())
    
    
write !
    
zwrite %sessionData
  
}
  
catch(ex){
    
write ex.DisplayString(),!
  
}
}

}

Vitaliy Serdtsev · Oct 8, 2020 go to post
Try it with between 1 and 5 on the %VID. Just as slow.
I ran your code with new index. count(prop1=2) = 2629833

%VID BETWEEN 1 AND 5 .000293 secs

%VID BETWEEN 2629829 AND 2629833 3.63472 secs

I can't create an index on every possible filter and order by combination.
Waiting for @Kyle Baxter response.
Vitaliy Serdtsev · Oct 8, 2020 go to post

Try adding a new index and don't forget make rebuild index/tunetable/recompile class

Index idx On (prop1, prop3) [ Type = bitmap ];

Here yet need the help of @Kyle.Baxter.

PS: by the way, check

select count(*from mp.test where prop1=2

to insert the correct values in the code

%VID BETWEEN 3000000 AND 30000005

Because of this, is obtained

0 Rows(s) Affected