¡Hola a todos!
Descargué la biblioteca de componentes
Mercury Database Objects —MDO— (la misma versión que está usando
Sick Boy, según me comentó), para echar un vistazo con mayor cercanía, específicamente sobre lo que se ha comentado de las unidades
MDOSQL.pas y
MDOUtils.pas (lo del nombre de cursor aleatorio, vaya), comparando diferencias con las unidades equivalentes nativas
IBSQL.pas y
IBUtils.pas de los IBX.
Cita:
Empezado por Sick boy
...intenta establecer el nombre del cursor con una llamada call
Código Delphi [-]
procedure TMDOSQL.ExecQuery;
var
fetch_res: ISC_STATUS;
begin
...
Call(
isc_dsql_set_cursor_name(StatusVector, @FHandle, PChar(FCursor), 0),
True);
FCursor debe tener la informacion que buscamos, asi que hago una busqueda en el codigo y encuentro esto en el CONSTRUCTOR de TMDOSQL:
Código Delphi [-]
constructor TMDOSQL.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
...
buffff, atentos a esto: "And, in some special situation, duplicate cursornames will appear" !!!!!
Justo lo que me sucede ahora, ya que muchas querys las creo dinamicamente...
|
Es curioso esto que encontraste, por varias razones:
1. El nombre de un componente (propiedad Name) es una cadena vacía durante su construcción. A menos que el constructor realice, directa o indirectamente, una asignación de valor a la propiedad Name, no tiene sentido usarla como parte de una expresión dentro del constructor.
2. Los creadores de MDO movieron esa sentencia al método Prepare, como bien lo señalaste, pero en los IBX sí está habilitada en el constructor de TIBSQL, por lo que, en ese caso, el nombre del cursor siempre será solamente lo que devuelva la función RandomString (un número aleatorio de 8 dígitos). Un dato importante para los usuarios de IBX.
3. Esto me hace pensar, y trato de recordar si lo que voy a mencionar es cierto, que en versiones más antiguas de Delphi, al agregar un componente a un formulario o módulo de datos en tiempo de diseño, dicho componente obtenía su nombre predeterminado ("Label1", "IBSQL1") por medio del constructor Create de TComponent.
Si alguien tiene Delphi 5 o anterior y algo de tiempo para comprobar ésto definiendo e instalando una clase de componente de prueba que muestre su nombre como última sentencia del constructor, se lo agradecería.
4. Los creadores de MDO movieron la sentencia al método Prepare, con el argumento de "
if you create the MDOSQL dynamically, the name is not assigned in the create and the cursor is only based on a 'random' number". Es claro que se dieron cuenta de lo mismo que comenté anteriormente (desde X versión de Delphi, o quizá desde siempre, la propiedad Name no tiene valor durante la ejecución del constructor). Pero asumen, o dan por recomendado, que debe asignarse un valor a la propiedad Name del objeto antes de ejecutar la consulta para que el nombre del cursor quede más rico y sea menos probable que se repita, lo cual es sencillo si el componente es agregado en tiempo de diseño o lo instanciamos nosotros mismos y enseguida le damos valor a Name. Sin embargo, la clase común TMDOCustomDataSet, al igual que sucede en los IBX, crea varias instancias internas de estos objetos SQL sin darles valor a su propiedad Name (a no ser que lo haga en algún punto del código que no logro ver). Así pues, con el nombre del cursor (FCursor) generado en el constructor Create o en el método Prepare, éste siempre será un número de 8 dígitos, a menos que se trate de un componente TMDOSQL que nosotros mismos hayamos preparado.
Cita:
Empezado por Sick boy
...
Vale, ya tenemos localizado el problema, los creadores de MDO ya lo sabian, veamos como lo resolvieron....
Código Delphi [-]
procedure TMDOSQL.Prepare;
var
stmt_len: Integer;
res_buffer: array[0..7] of Char;
type_item: Char;
begin
if FCursor = '' then FCursor := Name + RandomString(8);
En mi caso, los cursores que dan errores solo lo forman 8 numeros, el "Name" parece no contener ningun valor.
|
Esto va acorde con lo que comenté en el punto #4, pero no demerita el cambio que hicieron. Cuando agregues tú mismo un componente TMDOSQL a un módulo de datos, a éste le servirá realmente ese cambio. Entiendo que en la práctica esto parece poco útil, porque principalmente se usan derivados de TMDOCustomDataSet que crean instancias internas y sin nombre de objetos TMDOSQL, pero acabo de encontrar algo al respecto que te agradará (lo menciono más abajo
).
Cita:
Empezado por Sick boy
...Estas son las funciones que generan el nombre (numero) del cursor:
Código Delphi [-]
function RandomString(iLength: Integer): String;
begin
result := '';
while Length(result) < iLength do
result := result + IntToStr(RandomInteger(0, High(Integer)));
if Length(result) > iLength then
result := Copy(result, 1, iLength);
end;
function RandomInteger(iLow, iHigh: Integer): Integer;
begin
result := Trunc(Random(iHigh - iLow)) + iLow;
end;
Ahora mi duda es la siguiente, ¿Garantizan estas funciones numeros aleatorios para los cursores??
Si no son seguras, se pueden mejorar???...
|
Son las mismas funciones en IBX. Sí garantizan números aleatorios, pero no únicos. Las probabilidades parecen ser bajas (1 en decenas de millones), pero, ahora que existen los
GUIDs, podría ser preferible usar las funciones
CreateGUID y
GUIDToString de la unidad SysUtils.
Cita:
Empezado por Sick boy
...Por otro lado, si el problema no es por la generacion del numero de cursor, entonces es que un cursor se queda "pillado", pero no se como puede ser posible...
|
Eso necesitaría de otra línea de investigación. Pero dinos una cosa: en las máquinas donde falla, ¿cuántas veces por día u hora tu programa realiza una consulta Select sobre la base de datos? Una cifra estimada.
Cita:
Empezado por coso
Código:
// Removed the code FCursor set name from the TMDOSQL.Create.
// This is needed because if you create the MDOSQL dynamically, the name is
// not assigned in the create and the cursor is only based on a 'random'
// number. And, in some special situation, duplicate cursornames will appear
//FCursor := Name + RandomString(8);
que cosa mas cutre...y lo peor de todo, lo dejan a sabiendas...
yo de ti no perderia mas el tiempo con estos componentes...
|
Con todo respeto, creo que fue algo precipitado ese comentario amigo Coso. Por lo que expliqué antes, me parece que fue un acierto del autor mover esa parte del código al método Prepare, que si bien no es una solución brillante, al menos hará más seguro al componente cuando lleve un nombre. El texto no parece tener una buena redacción, pero lo entiendo como "
movimos esto a otro lugar, porque en este punto el componente no tiene nombre y por tanto el nombre del cursor sería solamente un número aleatorio que podría llegar a repetirse".
Cita:
Empezado por Sick boy
...Ya me gustaria saber cual es la "special situation" a la que se refieren...si al menos hubieran dicho cuales son las condiciones especiales se podria tratar de evitarlas...
|
Creo que te sales un poco de órbita. Sólo está diciendo que por ser un simple número aleatorio (sin el nombre del objeto como prefijo) es mucho más probable que coincida con el nombre de otro cursor abierto. Entiendo lo de "
special situation" como "
en algunas pero muy poco frecuentes ocasiones", y reitero que no fue nada malo ese cambio en el código.
Cita:
Empezado por Sick boy
...Parece que tendre que cambiar de componentes...
|
Aun cuando ya no suelo usar este tipo de componentes especializados en un motor (tarde o temprano te enamoras de dbExpress con TClientDataSet
), creo que cambiar de componentes sería algo aventurado en este momento.
Cita:
Empezado por Sick boy
...Afortunadamente, solo un cliente tiene esas condiciones especiales...
|
¿Será que él es el único que hace un uso extensivo de la aplicación? Vuelvo a mi pregunta de más arriba: ¿cuántas consultas realiza el programa en una sola sesión de ese cliente? Y, por casualidad, ¿no estará alguna parte del programa jugando con la variable
RandSeed?
Cita:
Empezado por Sick boy
...No me atrevo a meterle mano al codigo de MDO, pero esta claro que de alguna manera debo modificar el nombre del cursor para hacerlo unico, pero no se como hacerlo...
|
Podrías modificar el código fuente de MDO, pero, para evitar conflictos de versiones cuando compartas o actualices tu código, sería más sencillo asignarle un valor a la propiedad
QSelect.Name de tus componentes de consulta antes de que sea llamado el método Prepare, buscando, desde luego, que sean nombres únicos los que establezcas. Esto puedes hacerlo en el evento OnCreate del módulo de datos, o justo después de crearlos en tiempo de ejecución. Sólo toma en cuenta que la propiedad QSelect (que es el objeto TMDOSQL de la discordia), está protegida en algunas clases como TMDOQuery (al parecer no en TMDODataSet). Pero puedes usar el típico truco de molde de tipo para acceder a esa propiedad en caso de ser necesario.
Cita:
Empezado por Sick boy
...me es muy dificil reproducir el error, en mis equipos no pasa...
|
Te sugiero emplear la misma función, RandomString, pero con un valor de 1 en lugar de 8, para intentar reproducir las condiciones que generan la excepción.
Cita:
Empezado por Lepe
...La generación de números aleatorios siempre han tenido ese fallo, pueden repetirse en "determinadas circunstancias"...
|
Totalmente de acuerdo. Aunque el fallo es querer usar números aleatorios cuando lo que realmente se busca obtener son números únicos. Por ello mi sugerencia de usar GUIDs.
Cita:
Empezado por Lepe
...Dicho sea de paso, si la función random hiciera su trabajo bien, los MDO no fallarían ...
|
Esto me llamó mucho la atención, Lepe. ¿Cómo debería hacer su trabajo la función Random?
Como punto y aparte, y a pesar del análisis anterior, yo no descarto que la causa del problema pueda ser otra cosa. Esperemos a ver qué nos trae
Sick Boy...
Un abrazo sin nombre.
Al González.
P.D. Algo más: ¿alguien conoce el ámbito que tienen los nombres de cursores? ¿Es por conexión (programa), por máquina cliente, globales para todos los clientes...? Pienso que deberían ser por conexión/transacción, pero quisiera confirmarlo.