Salut,
J'ai donc un morceau de script JMP assez complexe qui échoue d'une manière qui semble étrange, et j'aimerais comprendre pourquoi. Je m'excuse pour la complexité ici avant d'entrer dans le vif du sujet.
J'ai écrit une bibliothèque que nous appellerons sql_lib.jsl qui définit un espace de noms « sql_lib ». Dans cet espace de noms, j'ai défini une classe clsQueryBatch avec le nom « sql_lib:QueryBatchService ». Le but de la classe est de permettre un traitement par lots facile de plusieurs requêtes SQL à exécuter simultanément en arrière-plan. L'idée est que lorsque l'on instancie un objet de type sql_lib:QueryBatchService, on peut alors ajouter des requêtes à l'objet, puis appeler la méthode de classe run_queries() pour les lancer toutes, en acquérant enfin les résultats de toutes les requêtes avec la méthode de classe get_results(). L'implémentation complète de cette classe n'est pas vraiment pertinente, mais ma méthodologie est la suivante : la méthode _ init _() de la classe définit quelques variables membres à partir des paramètres passés, comme la session de base de données à utiliser et le niveau de visibilité souhaité des tables de résultats, et surtout, elle crée également un espace de noms anonyme this:m_ns qui est l'endroit où je stocke et récupère les résultats des requêtes ainsi que le nombre de requêtes terminées/erronées. La méthode _ init _ ressemble à ceci (notez que j'ai également créé une classe wrapper pour la connexion à la base de données appelée sql_lib:DBSession, dont le but est de donner un nom à chaque connexion à la base de données afin que je puisse suivre et réutiliser facilement la même connexion pour plusieurs requêtes, en me reconnectant en cas d'erreur) :
_init_ = Method({
db_session,
visibility = 1
},
If(! (db_session<<GETNAME></GETNAME>
Once the object is instantiated, I add queries to the queue with add_query():
<CODE class=" language-jsl">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():
<CODE class=" language-jsl">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.
<CODE class=" language-jsl">Show(ns:results,Type(ns:results),key,queryResult);
puts this in the log:
<CODE class=" language-jsl">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.
Grâce à Jasean du support JMP, il s'avère que cette erreur se produisait parce que lorsque j'ai initialisé le tableau associatif des résultats, j'ai utilisé la fonction AssociativeArray(). Le fait de modifier cela pour utiliser la notation [=>] à la place a résolu mon problème.
Lorsque vous appelez AssociativeArray(), il renvoie apparemment l'objet constructeur, donc chaque fois que j'instanciais un nouvel objet, le tableau associatif était écrasé. En affectant statiquement la variable membre à un objet [=>], il n'est pas écrasé, du moins c'est ce que j'ai retenu de l'e-mail de @Jasean , peut-être qu'il peut apporter plus de lumière.