Hi,
So, I have a fairly complex piece of JMP script that is failing in a way that seems odd, and I’d like to understand why. I apologize for the complexity up front here before I get into it.
I have written a library we’ll call sql_lib.jsl that defines a namespace “sql_lib”. Within that namespace I’ve defined a class clsQueryBatch with name “sql_lib:QueryBatchService”. The purpose of the class is to enable easy batching of multiple sql queries to run in the background simultaneously. The idea is that when one instantiates an object of type sql_lib:QueryBatchService, one can then add queries to the object and then call the class method run_queries() to launch them all, finally acquiring the results of all the queries with the class method get_results(). The full implementation of this class isn’t really relevant, but my methodology is as follows: the _init_() method of the class sets up a few member variables from the passed parameters, like the database session to use and the desired visibility level of the result tables, and importantly, it also creates an anonymous namespace this:m_ns which is where I store and retrieve the results of the queries along with the number of completed/errored queries. The _init_ method looks like this (note I’ve also created a wrapper class for the Database Connection called sql_lib:DBSession, whose purpose is to give each database connection a name so I can track and reuse the same connection for multiple queries easily, reconnecting when there’s an error):
_init_ = Method({
db_session,
visibility = 1
},
If(! (db_session<<GetName == "sql_lib:DBSession"),
Throw("Invalid parameter for sql_lib:QueryBatchService. db_session must be of type sql_lib:DBSession.");
);
this:m_visibility = visibility;
this:m_dbc = db_session:connection;
this:m_dbc_name = db_session:name;
// use an anonymous namespace to track query results
// this is so we can access those results outside of the OnRunComplete and OnError contexts
Eval(
EvalExpr(
this:m_ns = NewNamespace(
{
dbc = Expr(m_dbc) ; // database connection
total = Expr(NItems(m_queries)) ; // total # of queries
complete = 0 ; // # of completed queries
error = 0 ; // # of errored queries
results = AssociativeArray() ; // list of resulting data tables
}
)
)
);
this:m_ns_name = this:m_ns << GetName;
);
Once the object is instantiated, I add queries to the queue with add_query():
add_query = Method({query_str,query_name},
If(!IsString(query_str),
Write("clsQueryBatch:add_query requires an SQL query string as a parameter.\!N");
Throw();
);
query = NewSQLQuery(Connection(m_dbc), CustomSQL(query_str));
query << Query Name(query_name);
m_queries[query_name]=query;
m_ns:total++;
);
And I can then launch all queued queries with run_queries(). This is where the weirdness happens. You’ll notice that I’ve created an Associative Array variable in the anonymous m_ns namespace called results. This should be available as m_ns:results, and I take advantage of this by wrapping the code that runs the queries in Eval(EvalExpr()) and injecting the m_ns_name member variable as an expression into the OnRunComplete method of the RunBackground message sent to each query. I recently changed m_ns:results from a List to an Associative Array because I needed to be able to ensure that I could track down multiple differently named result tables. When I was using a list to track the results, this code worked fine, but when I try to assign a value into the Associative Array, I get the error “Not subscriptable value in access or evaluation of 'Assign' , ns:results[key] = /*###*/queryResult/*###*/“. Preamble completed, here’s the code for run_queries():
run_queries = Method({},
If(m_visibility==1,
ForEach({{query_name,query},idx}, m_queries,
Eval(
EvalExpr(
query << RunBackground(
OnRunComplete(
Write("Query complete.\!N");
ns = Namespace(Expr(m_ns_name));
key = Eval(Expr(query_name));
Show(ns:results,Type(ns:results),key,queryResult);
ns:results[key] = queryResult;
ns:complete++;
),
OnRunCanceled(
Write("Query cancelled.\!N");
ns = Namespace(Expr(m_ns_name));
ns:error++;
),
OnError(
Write("Query failed!\!N");
ns = Namespace(Expr(m_ns_name));
ns:error++;
)
)
)
)
)
, //else
ForEach({{query_name,query},idx}, m_queries,
Eval(
EvalExpr(
query << RunBackground(
OnRunComplete(
ns = Namespace(Expr(m_ns_name));
key = Eval(Expr(query_name));
Show(ns:results,Type(ns:results),key,queryResult);
ns:results[key] = queryResult;
ns:complete++;
),
Invisible,
OnRunCanceled(
Write("Query cancelled.\!N");
ns = Namespace(Expr(m_ns_name));
ns:error++;
),
OnError(
ns = Namespace(Expr(m_ns_name));
Write("Query failed!\!N");
ns:error++;
)
)
)
)
)
)
);
As you can see, I’ve put a few Show() statements in here to try to understand what’s happening. The results of those are what prompts my question. Please also note that the query runs and generates a data table as expected, the problem arises when I try to store a reference to that data table for later use.
Show(ns:results,Type(ns:results),key,queryResult);
puts this in the log:
ns:results = [=>];
Type(ns:results) = "Associative Array";
key = "subid_temp_table_data";
queryResult = DataTable("subid_temp_table_data");
so clearly the namespace is resolving correctly, and the type of ns:results is Associative array, and key is a string, queryResult is a datatable, but for some reason I am unable to create a new key=>value pair in the ns:results AA. Remember, this worked when ns:results was a list and I used Insert Into(ns:results, queryResult). Is there a difference in the memory context for lists and associative arrays that I’m violating in some way here? Usually in the past when calling RunBackground and using the OnRunComplete context, I’ve used a global variable to hold the results of the query, is this an issue with my nested namespace:class:namespace structure? Any insight you can provide would be greatly appreciated.
Robert Maltby