Kyle Baxter · Jun 14, 2017 go to post

I think it is important to remember that Row Level Security typically results in worse performance.  It is better to create a VIEW and give your users access to the VIEW instead of going through this exercise.  

Kyle Baxter · Jun 10, 2017 go to post

Arockia,

This sounds like something you might want to discuss with the WRC (which is what I do!).  You will need to send over a sample table and some test data showing the problem and we'll be able to sort you out in no time.

 

Cheers!
Kyle

Kyle Baxter · Jun 8, 2017 go to post

I'm unsure, exactly, what you're trying to do here.  You could write a Stored Procedure that returns the value of a global pretty easily.  If you need to run code before connecting via ODBC you can use "init code" set up via the $SYSTEM.SQL.SetServerInitCode() API.  I'm not sure exactly what you're looking for, though.

Kyle Baxter · Jun 5, 2017 go to post

So the problem here was likely that you either didn't have the method as an Expression or you weren't QUITting a value.  If you have a method that you intend to use as a Webmethod (SOAP method) then you must return a value.  Your options are:

Method AddInteger(Arg1 As %Integer, Arg2 As %Integer) As %Integer [ WebMethod, Codemode=Expression ]
{
Arg1 + Arg2
}

or

Method AddInteger(Arg1 As %Integer, Arg2 As %Integer) As %Integer [ WebMethod ]
{
      QUIT Arg1 + Arg2
}

I'd guess you weren't quitting out the value.

Kyle Baxter · Jun 2, 2017 go to post

Wellll technically NULL is a valid value for all datatypes.  I think what you want to do is to interrogate the metadata information which you can do like so:

SAMPLES>s stmt=##class(%SQL.Statement).%New()

SAMPLES>w stmt.%Prepare("SELECT ID,Name FROM Sample.Person")
1
SAMPLES>w stmt.%Metadata.%Display()
(You'll get lots of output)

Note that you can do this even without running the query!  You can look at %SQL.StatementMetadata in the Class Reference for more methods and properties that can help you.  

Note 2 - Sean's method is also perfectly valid.  The only advantage to mine is that you don't have to run the query.

Kyle Baxter · May 30, 2017 go to post

I agree with Mark.  If you do not understand his response and/or if you still have questions, I invite you to open an issue at wrc.intersystems.com and the Support Team will help you get to the bottom of this.

Kyle Baxter · May 10, 2017 go to post

While I'm not entirely sure what you are trying to do here as MLLP is a bit foreign to me, the CSP Gateway is just a module added onto a web server.  If you can get the setup you want with a webserver, you can use the CSP Gateway to talk to Caché/Ensemble/HealthShare .  What exactly are you trying to accomplish here?  What's your goal?

Kyle Baxter · May 10, 2017 go to post

If by the 'Web Services testing part' you mean a page like this:

http://localhost:57772/csp/samples/SOAP.Demo.cls

That doesn't even use SOAP, it uses an HTML form submission to run the method directly.  What that page does is test the logic of your webservice methods in a simple way, to make sure your desired inputs give the desired outputs.  You should use something like SoapUI if you want to more completely test your SOAP methods.

Kyle Baxter · May 10, 2017 go to post

This is not documented because we don't want new development using this kind of storage (which we call "SQL Strorage").  We'd much prefer you to use the standard storage strategy that you get for free when creating classes.   If you look at Brendan's 5th (of 3) articles, you'll see a link with the text "Get the Examples here" where you can download some examples (if that works, the link will be here as well). 

Again, if you can avoid doing this, I recommend using standard storage.

If you run into any problems or if you have any questions, I strongly suggest you contact the WRC.  

Kyle Baxter · Apr 25, 2017 go to post

How does it connect - via ODBC/JDBC or is there another connection mechanism?

Kyle Baxter · Apr 20, 2017 go to post

Using the macros is a bad idea.  The changes to the JSON, especially in this case, are simple.   Any source control system worth its salt is going to be able to make nearly all of the changes for you automatically.  As far as I know the only change that can't be caught by find-and-replace is the one requiring COS expressions embedded within JSON to have parentheses.  In this case, the new syntax is supported in 2016.1 JSON, so you wouldn't even need to change that.  Because of the ease of change, the fact that the change only needs to happen once, and the code obfuscation that occurs when adding  macros, I do not think using them is a good idea for anyone planning to use Caché's native JSON implementation.

 

Edit: It occurs to me that the macros would be useful if you have to switch back and forth between 2016.1 and later versions over and over again, but to do the conversion once is too simple to bother with macros.

Kyle Baxter · Apr 18, 2017 go to post

Are you using a trigger generator or writing your own code?  Can you send the whole trigger definition for review?

Kyle Baxter · Apr 1, 2017 go to post

There is no such command.  I would recommend moving the index global as well.  However, if this is not an option then you probably want to do something like:

USER>s sql="select Name from %Dictionary.CompiledClass where system=0 and NOT(Name %Startswith '%') AND SUPER [ 'Persistent'"     


USER>s rs=##class(%SQL.Statement).%ExecDirect(,sql)                                                                               

USER>while rs.%Next() { s sc= $classmethod(rs.%GetData(1),"%BuildIndices") w "Built "_rs.%GetData(1)_" with return code = "_sc,! }

Obviously this is kind of hacked together and you'll want to clean it up, but the outline is there.  I had to make some assumptions like you don't have any other classes in your application that have 'Persistent' in them.  If you do then you'll want (Super [ '%Library.Persistent' OR Super [ '%Persistent').  Since classes are case sensitive this should be fine. 

Kyle Baxter · Mar 30, 2017 go to post

Triggers are fired after data normalization and validation.  Do you think you can/should use your own datatypes, overriding the Normalize or LogicalToDisplay/DisplayToLogical functions.  If that doesn't make sense you could have a field calculated off of the stored data?  There are lots of options here, the best one is going to depend strongly on the goals you have for the project.

Kyle Baxter · Mar 23, 2017 go to post

These answers are all sufficient.  However, perhaps a more natural solution is to make the field of type %Date.  Then you can use the SelectMode to determine how the date is represented in a query.  For instance, look at the DOB field in Sample.Person (in the SAMPLES) namespace.  The storage looks like this:

^Sample.PersonD(1)=$lb(","Waterman,Brenda Z.","437-96-2023",36088,...)

Here we see the ID (1) the Name ("Waterman,Brenda Z.") the SSN (437-96-2023), and the DOB (36088).  If you want to see the data you can do the following:

--------------------------------------------------------------

SELECT Name,DOB FROM Sample.Person WHERE ID=1

Name                                     DOB

Waterman,Brenda Z.      36088

--------------------------------------------------------------

If we wanted to see that in a readable format, we can change the selectmode and get:

--------------------------------------------------------------

Name                                       DOB

Waterman,Brenda Z.   1939-10-22

--------------------------------------------------------------

Moreover, you can use this in the WHERE clause very naturally:

--------------------------------------------------------------

SELECT Name,DOB FROM Sample.Person WHERE DOB='1939-10-22'

Name                                           DOB

Waterman,Brenda Z.     1939-10-22

Underman,Jane V.          1939-10-22

Bush,Susan E.                   1939-10-22

--------------------------------------------------------------

The way to change the selectmode is going to depend on how you are running your query.  All queries that come through xDBC are in ODBC mode, so that's easy.  If you are running a dynamic SQL statement you can do:

s stmt.%SelectMode=1  

Possible values are:

  • 0 for LOGICAL mode.
  • 1 for ODBC mode.
  • 2 for DISPLAY mode.

If you are using embedded SQL, you can use the precompiler directive #SQLCompile select=<value> as outlined here:

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

Kyle Baxter · Mar 18, 2017 go to post

Eduard is right, you should use GREATEST in this case.  CASE statements are kind of sloppy, and while they are fine to use, I like to avoid them in order to keep the query as readable as possible.

More to your other points, you can find a list of valid SQL functions here:

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

Note that all $-functions are not SQL Standard and will only work with Caché (most likely), so if you are interested in database agnosticism then try to use one of the more standard functions.  Good luck! 

-Kyle

Kyle Baxter · Feb 24, 2017 go to post

Yup!

DELETE FROM Sample.Person where (ID # 2) = 0   --Deletes all even numbered rows (so you know I'm not cheating and using ID numbers)

select * from (select id,name,ssn from sample.person) where %vid between 11 and 20 

%VID is a special variable in Cache SQL (VIEW ID, I think?) which allows you limit your rows without too much trouble.  And alternative is the following:

select TOP 10 ID,Name,SSN from Sample.Person where ID NOT IN (SELECT TOP 10 ID FROM Sample.Person)

The first TOP gives you your page size, and the subquery's TOP gives you where to start (in this case, we start at 11, because we're ignoring the first 10).  You can dynamically put in the sizes for TOP.  

Kyle Baxter · Feb 15, 2017 go to post

SIMPLEST?  %String, definitely.  You can even use the ['whatsup@doc.com' syntax to search if you have to.  Using List of %String is a messy way to do anything in the relational world.  

If you do need to index them later, I'm pretty sure you can use iFind to do this without too much trouble (although you would need a new license, potentially).

Kyle Baxter · Feb 8, 2017 go to post

Cache 2010.2 does not support Java 7:

http://docs.intersystems.com/cache20102/csp/docbook/DocBook.UI.Page.cls…

You'll need to use an older version of Java or a newer version of Caché.

Note: Java 7 was released in July 2011, so obviously we couldn't support it a year before its release.

P.S. Sorry it took you so long to get an answer!  If ever you need something more quickly, please feel free to contact the WRC at support@intersystems.com.  

Kyle Baxter · Feb 3, 2017 go to post

Example!

Class:

------------------------------------------

Class Jiri.RegisteredObject extends %RegisteredObject {

Property p1;

Property p2;

}

------------------------------------------

Terminal:

------------------------------------------

SAMPLES>s j=##class(Jiri.RegisteredObject).%New()

SAMPLES>s j.p1="test"

SAMPLES>s j.p2="json"

SAMPLES>w ##class(%ZEN.Auxiliary.jsonProvider).%WriteJSONStreamFromObject(.stream,j)

1

SAMPLES>w stream.Read()

{

"_class":"Jiri.RegisteredObject",

"p1":"test",

"p2":"json"

}

------------------------------------------

Kyle Baxter · Feb 3, 2017 go to post

Do your CSP Files show up in the Server Explorer portion of Atelier?  Also, what is your Atelier version (Atelier->About Atelier)?

P.S. I think that was the most I've ever written "Atelier" in a short time.

Kyle Baxter · Feb 1, 2017 go to post

I'm pretty sure you need to return an oref to get this working.  Here's one that I was able to get working:

Class Sample.SerialGet Extends %Persistent {
Property Address as Sample.Address [calculated];

Method AddressGet() as Sample.Address {  
    set a=##class(Sample.Address).%New()  
    set a.City="Cambridge"  
    set a.State="MA"  
    set a.Zip="02142"  
    set a.Street="1 Memorial Drive"  
    quit a
}
}

Terminal:

SAMPLES>s reader=##class(%XML.Reader).%New()

SAMPLES>w reader.OpenFile("/users/kbaxter/downloads/testopensinglequote.xml")

1

SAMPLES>s x=##class(Sample.SerialGet).%New()

SAMPLES>w x.Address

5@Sample.Address

SAMPLES>w x.Address.Street

1 Memorial Drive

Kyle Baxter · Dec 28, 2016 go to post

I think the best way to do this would be to write something in COS, write a class query with it, and use that as a Table Valued Function.  While there might be a relational way to do this, it seems like it'd be messy.

Kyle Baxter · Dec 27, 2016 go to post

Caché does NOT support SQL-92 in its full implementation, so please refrain from saying so.  Example:

SELECT 3+3*3

Result: 18

Clearly, by not following order of operations we are violating the standard.  Moreover, despite not conforming perfectly to SQL-92 we do support plenty of extensions, such as Table-Valued Functions.

That said, Caché does not support WITH in the way you describe.  However, you can accomplish the same thing by using subqueries, VIEWs, or multiple queries.  You can also JOIN each record with its parent and filter on that.  If you give some more information I'd be happy to point you in the right direction.

Kyle Baxter · Dec 15, 2016 go to post

If you want to investigate the performance of the query you can always open a WRC Case (wrc.intersystems.com :-D).
Otherwise, send out your class definition and query plan and I can take a cursory look at it.

Kyle Baxter · Dec 15, 2016 go to post

I actually baked a cake to commemorate this occasion, and totally not because I'm fat and wanted cake.

Kyle Baxter · Dec 2, 2016 go to post

Well, if you want good performance in SQL Queries, you need indexes.  What do your tables look like?  We can definitely get fast performance on a 50M row table, it might just take some work (which we're happy to help with).

Kyle Baxter · Nov 21, 2016 go to post

Sure - you can try to merge it off before deleting it:

m ^KMB = ^%sqlcq("NSP","SMPQueryHistory")
 

And then you have your query history in a different global, which you can reference when needed.