Hola,
Tengo un fragmento bastante complejo de un script JMP que falla de una manera que parece extraña y me gustaría entender por qué. Pido disculpas por la complejidad antes de comenzar.
He escrito una biblioteca que llamaremos sql_lib.jsl que define un espacio de nombres “sql_lib”. Dentro de ese espacio de nombres he definido una clase clsQueryBatch con el nombre “sql_lib:QueryBatchService”. El propósito de la clase es permitir la fácil agrupación de múltiples consultas SQL para que se ejecuten en segundo plano simultáneamente. La idea es que cuando uno crea una instancia de un objeto de tipo sql_lib:QueryBatchService, puede agregar consultas al objeto y luego llamar al método de clase run_queries() para iniciarlas todas, adquiriendo finalmente los resultados de todas las consultas con el método de clase get_results(). La implementación completa de esta clase no es realmente relevante, pero mi metodología es la siguiente: el método _init_ () de la clase configura algunas variables miembro a partir de los parámetros pasados, como la sesión de base de datos a utilizar y el nivel de visibilidad deseado de las tablas de resultados, y lo que es más importante, también crea un espacio de nombres anónimo this:m_ns que es donde almaceno y recupero los resultados de las consultas junto con la cantidad de consultas completadas/con errores. El método _init_ se ve así (tenga en cuenta que también he creado una clase contenedora para la conexión de base de datos llamada sql_lib:DBSession, cuyo propósito es dar a cada conexión de base de datos un nombre para que pueda rastrear y reutilizar la misma conexión para múltiples consultas fácilmente, volviendo a conectarme cuando hay un error):
_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.
Gracias a Jasean del soporte de JMP, resulta que este error se producía porque, cuando inicialicé la matriz asociativa de resultados, utilicé la función AssociativeArray(). Cambiar esto para utilizar la notación [=>] resolvió mi problema.
Cuando llamas a AssociativeArray() aparentemente devuelve el objeto constructor, por lo que cada vez que estaba instanciando un nuevo objeto, la matriz asociativa se sobrescribía. Al asignar estáticamente la variable miembro para que sea un objeto [=>], no se sobrescribe, al menos esa fue la conclusión que saqué del correo electrónico de @Jasean , tal vez él pueda arrojar más luz.