Académique Documents
Professionnel Documents
Culture Documents
3. MODELOS DE BD
T1 W(A) ABORT
T2 W(A) COMMIT
Lectura no repetible: Tiene lugar cuando una transacción lee dos veces el
mismo dato y obtiene valores diferentes a causa de una modificación efectuada
por una segunda transacción simultánea.
T1 R(A) R(A) COMMIT
T2 R(A) W(A) COMMIT
Bases de datos 2 · 5
En este ejemplo de transferencia entre dos cuentas A1 y A2, los datos que
lee T2 no son correctos, corresponden a un estado intermedio que no
deberían haberse leido fuera de la transacción T1.
Lectura no
repetible y
Actualización Lectura no análisis de
Fantasmas
Perdida confirmada inconsistencias
(excepto
fantasmas)
READ
Sí No No No
UNCOMMITED
READ
Sí Sí No No
COMMITEED
REPEATABLE
Sí Sí Sí No
READ
SERIALIZABLE Sí Sí Sí Sí
T1 R(A) W(A)
T2 R(A) W(A)
T1 R(A) R(A)
T2 W(A)
Vamos a comentar todo este proceso del grafo de precedencias con un ejemplo
que encontramos al margen de la página siguiente. Al comenzar a procesar el
horario, vemos que las acciones 30 y 70 no son conmutables, dado que si se
efectúa antes W(C), la R(C) variará, por tanto dibujamos un primer arco en el
grafo, con el valor del gránulo afectado (C) y como nodos las transacciones, parte
Bases de datos 2 · 7
Horario no serializable
primero de T1 que es la primera que se realiza y la flecha llega a T2 que es la
# T1 T2
10 R(A) segunda.
20 R(B)
30 R(C)
40 R(B)
50 R(E)
60 R(C)
70 W(C)
80 W(B)
90 W(E) Si continuamos avanzando por el horario de ejemplo, llegamos a encontrar
100 W(A) mas acciones no conmutables, la 40-60, 80-110, 90-120 y 70-130, con lo que el
110 W(B)
120 R(E) grafo final nos queda de esta forma:
130 R(C)
140 Commit
150 Commit
Horario de ejemplo serializable Ponemos ahora otro nuevo ejemplo, que vemos en la figura lateral y
# T1 T2 T3 T4 al desarrollar el grafo de precedencias, obtenemos:
10 R(A)
20 W(A)
30 R(A)
40 R(B)
50 W(B)
60 R(A)
70 R(B)
80 R(D)
90 W(D)
100 R(E)
110 W(E)
120 R(F)
130 R(D)
140 Commit
150 Commit
160 Commit Como el grafo es acíclico, no existe ninguna interferencia, por lo que
170 Commit ahora sí podemos encontrar los horarios seriales equivalentes. Debemos
comenzar con un nodo Terminal, en este caso, solo existe T1; lo
eliminamos, junto con las flechas que de él parten y nos queda:
Procedemos ahora igual con T2, y luego podemos escoger indistintamente T3 ó T4,
con lo cual los dos horarios seriales equivalentes serían: T1, T2, T3, T4 y también
T1, T2, T4 y T3.
T1 W(A) ABORT
T2 W(A) COMMIT
8 · Bases de datos 2
T1 W(A) ABORT
T2 W(A) COMMIT
T1 R(A) W(A)
T2 R(A) W(A)
T1 W(A) ABORT
T2 W(A)
Para resolver estos abrazos, los SGBD pueden optar por una de las tres
posibilidades siguientes:
Hay muchas veces que se dispone de múltiples niveles de Jerarquía de composición de gránulos
granularidad y esto afecta obviamente a los protocolos
implementados en el punto anterior. Pueden existir jerarquías de
gránulos donde gránulos de dimensiones mayores pueden contener
subgránulos, o gránulos más pequeños. Así una reserva S o X de un
gránulo puede permitir acceder a los más pequeños sin llevar a cabo
nuevas reservas.
Así, si una transacción tiene que acceder a todos o a una gran
parte de los gránulos elementales que contiene otro gránulo, puede
incrementar el rendimiento haciendo menos peticiones de reserva, si reserva el
gránulo completo. Para leer todos los datos de la tabla T1 lateral, la transacción
podría ser:
Niveles de aislamiento
3. RECUPERACIÓN
Con No wait, no espera; con WAIT puede esperar los segundos que se indiquen
antes de abortar o, si no se han puesto segundos, intentará continuar
indefinidamente. Al crear una tabla puede especificarse el nivel del gránulo por
registros (row) o por páginas (page) con la siguiente sintaxis:
En este sentido, para construir aplicaciones que puedan acceder a una BD,
se suele recurrir dentro del SQL a 3 aplicaciones:
Las sentencias SQL se mezclan con las del programa anfitrión, indistintamente
en mayúsculas o minúsculas.
Las sentencias SQL van precedidas de la clave EXEC SQL.
14 · Bases de datos 2
Hay que marcar el final de las sentencias de SQL hospedado, en lenguaje C por
ejemplo con ;.
Se declara el área de comunicaciones SQLCA que sirve para que el SGBD
informe al programa de los diferentes errores que pueden producirse en tiempo
de ejecución. Así se puede evaluar el éxito o fracaso de una sentencia SQL y
continuar el programa en consecuencia.
Se declaran variables puente con SQL con BEGIN DECLARATE SECTION y END
DECLARATE SECTION; además se debe prefijar el nombre de la variable con
dos puntos :nombre_variable. Podemos declarar estas variables al comienzo del
programa o en estructura de bloques como el lenguaje C, donde entonces la
visibilidad de cada variable queda restringida al bloque en que se declaró.
Hay que tener en cuenta además que el tipo de datos reconocido por el
lenguaje SQL y el lenguaje anfitrión puede ser distinto; amén del
reconocimiento de los valores nulos. Para evitar este último problema, cada
variable puente SQL tiene que tener una variable indicadora asociada si la
columna con la que está relacionada admite valores nulos. Así un valor
indicador igual a 0 significa que el valor no es nulo y que la variable puente
contiene el valor buscado; un valor indicador negativo significa que queremos
asignar un valor nulo para la columna que tratamos, con lo cual el valor real de
la variable puente es irrelevante; por último un valor indicador positivo indica
que la variable puente tiene un valor válido pero que puede haber sido
redondeado, truncado, etc.
Gestión de errores: En SQL hospedado la gestión de errores es
responsabilidad de la aplicación. Los errores de código SQL son reconocidos por
el precompilador, pero los errores en tiempo de ejecución son tratados por dos
variables la SQLCODE y la SQLSTATE. Tenemos dos porque la variable inicial
para los errores era SQLCODE pero al no existir un acuerdo sobre el valor de
los distintos códigos en las diferentes SGBD, se optó por mantenerla de forma
histórica e inventar la SQLSTATE.
SQLCODE: Cuando se ha ejecutado la sentencia de SQL hospedado, se fija el
valor de la sentencia SQLCODE para indicar el estado de finalización de la
misma, si SQLCODE vale 0, la sentencia se ha ejecutado con éxito; si vale
negativo, significa que se ha producido un error grave que impide la ejecución
correcta de la sentencia; mientras que si vale positivo se ha producido una
situación de aviso, típica del redondeo o truncamiento (por ejemplo
SQLCODE=100 significa que no se han encontrado datos que cumplan las
condiciones especificadas por la sentencia).
SQLSTATE: Consta de cinco caracteres, los dos primeros de la cadena
identifican la clase de error y los 3 últimos la subclase.
Manipulación de una BD
Void insertar_empleado()
{
Int estado;
Printf(“Introduce el DNI del empleado: “);
Scan f (“%s”, dni_empl);
….
4. SQL Y JAVA
Tabla de compatibilidades
La solución propuesta por Java para el acceso y
manipulación de BD no parte de cero, sino que a partir de
SQL/CLI y de su implementación ODBC construye un sistema
fiel a los principios del lenguaje, es decir, simpleza y elegancia.
Java ofrece el JDBC que va más allá de lo que se
consigue con SQL/CLI que es ofrecer métodos para controlar el
posicionamiento de los cursores (previous, netxt…), métodos
para modificar el contenido de tablas sin utilizar SQL, permite la
realización batch de un conjunto de modificaciones, etc.
Los drivers
Es necesario contar con drivers que son una
implementación para poder trabajar con SGBD. Encontramos 4
tipos de drivers distintos (podemos ver los 4 tipos en la imagen
lateral):
permite llamadas directas al protocolo nativo de red del SGBD sin una capa de
software intermedio.
Para localizar y cargar el driver recordemos que debemos definir un PATH con
la ruta del mismo, el driver es ifkjdbc.jar y por supuesto el programa tendrá que
importar explícitamente el API del JDBC, a través de import java.sql.*. Hechas
estas dos cosas, ya estamos en condiciones de escribir las primeras líneas del
programa. Normalmente todo el código lo introduciremos en un bloque try-catch
para evitar errores de programa:
Tablas
Este es el componente lógico principal de la BD, y para crearla utilizaremos
la sentencia CREATE TABLE <nombreTabla> (definicionColumnas,
definicionRestricciones).
La definición de columnas incluye la definición del conjunto de columnas
que incorpora la tabla, separada por comas. Definiremos para la columna el
nombre, el tipo de datos, la definición por defecto y la restricción de columnas. El
tipo de datos puede ser byte, carácter, date…, la definición por defecto, si
queremos permiten especificar los valores que se asignan a cada columna por
omisión; mientras que las restricciones de columna están limitadas a una sola
columna no a todas las que forman una tabla determinada, y pueden ser NOT
NULL, DISTINCT, UNIQUE, PRIMARY KEY y CHECK.
Podemos borrar tablas mediante la sentencia DROP TABLE y podemos
modificarla añadiendo, eliminando columnas o restricciones mediante la sentencia
ALTER TABLE.
Vistas
Es el segundo componente lógico de datos de las BD. Una vista realmente
es una tabla derivada, su esquema y contenido derivan de otras tablas e incluso
de otras vistas, mediante una consulta.
Partiendo de ello podemos pensar que dado que las vistas realmente son
tablas, se deberían poder consultar y actualizar mediante sentencias SQL, y puesto
que son derivadas, su existencia no existe físicamente en la BD como las tablas
sino que se genera cada vez que se hace una petición a esa vista.
Para crear la vista tenemos la orden CREATE VIEW <nombreDeVista>,
columnas as <sentenciasSelect> [WITH CHECK OPTION], y para borrar la vista
DROP VIEW.
Por ejemplo sobre una tabla de departamentos podemos crear la vista:
CREATE VIEW DeptRicos AS
SELECT * FROM Departamentos WHERE presupuesto>240000
Este segundo aspecto puede ser fuente de problemas porque a veces el SGBD
dispone de diferentes alternativas para propagar un mismo cambio, y por ello no
sabe cual elegir y muchas vistas no son actualizables. Solo serán actualizables estos
dos casos:
La operación que queremos realizar en una operación de selección del álgebra
relacional sobre una única tabla o vista actualizable.
La operación es una selección + proyección de álgebra relacional siempre que
la proyección incluya todas las columnas clave (primaria o alternativas) y todas
las columnas que incorporen la restricción NOT NULL.
Así pues aquellas vistas que, por ejemplo, incluyan operaciones de combinación
de dos o más tablas o que incorporen funciones de agregación o la cláusula GROUP
BY no serán actualizables.
2.2. Disparadores
Los componentes vistos hasta ahora no son suficientes para modelar todo
lo que puede ocurrir en el mundo real. Si imaginamos una tabla de stocks de
productos y queremos aplicar una regla para que cuando un producto quede por
debajo de 50 unidades se produzca una petición de 100 unidades más, podemos
hacer dos cosas:
Añadir esta regla a todos los programas que actualizan el stock de los
productos. No es una solución muy convincente pues todo el código se
encontraría desperdigado por todo el programa, replicada y por lo tanto es
difícil de localizar y de cambiar.
Realizar un programa adicional que haga un sondeo periódico de la tabla
comprobando la regla. En este caso aunque la regla está concentrada en un
único punto, si el sondeo se hace ocasionalmente pierde su razón de ser y si se
hace muy a menudo se pierde eficiencia.
Para evitar esto la solución correcta es poder incorporar a la BD una regla del
tipo: “Cuando se modifique el stock de un producto y pase a ser menor que 50, hay
que pedir 100 unidades nuevas” y esto es realmente un disparador.
Para cada tabla solo se puede tener un solo disparador para INSERT, uno
para DELETE y otro para UPDATE de cada una de las columnas. Las acciones
especificadas como BEFORE se ejecutarán una sola vez antes que la sentencia que
activa el disparador; las de AFTER después de ejecutada la sentencia que la activa.
Las acciones FOR EACH ROW se ejecutarán una vez por cada fila afectada por la
sentencia que activa el disparador e inmediatamente después de haberla
modificado, borrado o insertado.
Cuando se ejecuta una sentencia SQL contra una BD puede pasar que se
activen disparadores y que se violen restricciones de seguridad a la vez. Frente a
esto, el sistema debe decidir qué hay que hacer en primer lugar e Informix retrasa
la comprobación de las restricciones de seguridad hasta después de la activación de
los disparadores asociados, lo que permite añadir disparadores que reparen
restricciones de integridad
2.3. Privilegios
Privilegios de BD
Cuando un usuario crea una BD tiene todos los privilegios sobre la misma y
será inaccesible a los demás usuarios hasta que lo autorice. Los privilegios que se
pueden conceder son:
Privilegios de Tabla
Cuando un usuario crea una tabla será inaccesible para los demás hasta
que les conceda privilegios sobre ella. Existen 7 privilegios sobre las tablas:
Insert
Delete
Select o bien Select de un conjunto de columnas
Update o bien Update de un conjunto de columnas
Referentes o bien Referentes de un conjunto de columnas, que permite realizar
borrados en cascada.
Alter: Da derecho a añadir columnas y eliminarlas, cambiar el tipo de una
columna….
Bases de datos 2 · 23
Para conceder todos los privilegios se pude utilizar la palabra clave All; además en
la sintaxis de privilegios de una tabla:
Creamos el rol lector para poder leer en la tabla 1 y luego le asignamos ese
rol a Juan, además pudiendo él también conceder este privilegio al resto de
usuarios si lo desea.
Catálogo de una BD
3. CATÁLOGO
2. EL NIVEL LÓGICO
3. EL NIVEL FÍSICO
Existen otros tipos de páginas como son la página de índice, que aunque
lo hemos incluido en los elementos de control, realmente son componentes de
rendimiento para acelerar la búsqueda de filas determinadas dentro de la BD; eso
sí esta página de índice sigue la misma estructura que la estudiada hasta ahora.
Otro tpo de página es la de objeto grande muy en boga últimamente por la
irrupción de los elementos multimedia en la informática de consumo y se suelen
almacenar en otro tipo de estructuras diferentes dado su tamaño.
Gestión de páginas
Las principales operaciones que se utilizan son las siguientes:
La extensión
Es un número entero de páginas, en principio consecutivas, que el sistema
operativo adquiere a petición del SGBD cuando detecta que necesita más espacio
para almacenar datos. Así el fichero aumenta de tamaño porque aumenta el
número de extensiones que lo forman. Las páginas de una extensión tienen que ser
físicamente consecutivas, pues así mejora el rendimiento general de la BD.
El fichero
El fichero es la unidad que utiliza el sistema operativo para gestionar el
espacio e los dispositivos periféricos (también es un conjunto de extensiones). El
sistema de gestión de los ficheros viene a ser parecido al sistema de entrada/salida
tradicional que se realiza por medio de bloques, pero tiene algunas diferencias. Así
cuando el SGBD necesita cargar en su memoria principal unos datos que se
encuentran en una página que está en la memoria externa, se lo pide al sistema
Bases de datos 2 · 27
operativo, que toma esa página, y generalmente las circundantes por si las
necesita, y este número de páginas es pasado a la memoria intermedia.
4. EL NIVEL VIRTUAL
Correspondencia entre páginas virtuales y reales El espacio virtual es una visión diferente de
las páginas del nivel físico. Es otra forma de ver los
datos pero sin duplicarlos realmente; así el espacio
virtual es una secuencia de páginas virtuales que se
corresponden una a una con las páginas reales del
nivel físico pero ordenadas secuencialmente y sin
saltos en medio. Así en las páginas virtuales todos
los datos referentes a un mismo registro se
encuentra de forma contigüa pudiendo no ser así
en el nivel físico.
Cuando el SGBD necesita una fila
determinada (unidad mínima de información a la
que accede), lo que hace realmente es encontrar
las filas en el espacio virtual, el direccionamiento
del SGBD es pues del nivel lógico al virtual.
Posteriormente el sistema operativo hace una
relectura de ese espacio virtual al espacio físico
donde se hallan los datos. Es necesario hacer una serie de acotaciones ahora:
Una fila dada de alta y mientras permanezca viva, no puede cambiar de
página. Si otra fila se da de baja, puede reorganizar el espacio libre y cambiar el
apuntador de dentro de la página, pero sin cambiar de página. Si aumenta de
tamaño y hay que trasladarla a otra página, se mantiene un apuntador en la página
que señala la nueva dirección; de esta forma la BD seguirá teniendo un buen
rendimiento.
Espacio de tablas: Las tablas que no son tan grandes que las queramos
controlar por partes, ni tan pequeñas que puedan compartir un espacio común,
suelen tener un único espacio virtual de tablas; eso sí puede perfectamente
estar repartido en varios ficheros.
Espacio fragmentado: Cuando una tabla es tan grande que debemos partir
su espacio virtual se establecerá un criterio por el cual las diferentes filas de la
tabla irán a parar unívocamente a uno y otro fragmento, lo cual mejorará el
tiempo de acceso a los datos pues se “espera” donde poder encontrarlos.
Espacio de agrupación: Cuando una tabla se relaciona tanto con otra u otras
que normalmente el acceso a la primera viene unido del acceso al resto, nos
28 · Bases de datos 2
interesará que se encuentren físicamente tan cerca como sea posible para
minimizar el tiempo de acceso global y aumentar el rendimiento de la misma.
Espacio de objetos grandes: Ya hemos hablado de elementos multimedia,
entre otros, que ocupan un excesivo espacio. Estos objetos por ejemplo cuando
sus leídos por la entrada/salida del sistema operativo desplazarían todo el resto
de las páginas que se tienen cargadas en memoria y no interesa
evidentemente. Así el espacio que apuntan si por ejemplo es un fichero MP3 se
guarda en una página objeto, mientras que en la original solo se guarda un
apuntador a este objeto aparte.
Espacio de índices: Los índices son estructuras de datos de longitud muy
pequeña y muy referenciada, por lo que interesa gestionarlo por separado de
las filas de las tablas. Estructuralmente los espacios de índices son muy
similares a los espacios de tablas aunque contienen páginas de índices en vez
de páginas de filas de tablas.
Árboles B+
Los árboles B+ son un tipo particular de árbol de búsqueda cuyo objetivo
primordial es el de intentar conseguir que éstas se efectúen con un número
pequeño de entrada/salida.
Todo árbol B+ tiene un número de orden d, que indica la capacidad de
sus nodos, así si un árbol tiene orden d, entonces sus nodos contienen como
máximo 2d valores.
Los nodos internos tienen como objetivo dirigir la búsqueda de la hoja
que tiene la entrada correspondiente a un valor determinado, por ello el acceso
directo por valor consistirá en trazar un recorrido del árbol que empezará en la raíz
y que irá bajando por los nodos del árbol hasta llegar a la hoja adecuada. Todo
nodo interno contiene valores y apuntadores hacia sus nodos hijo.
Los nodos hoja están conectados por apuntadores, sirviendo así para
facilitar el acceso secuencial por valor, pero además contienen todas las entradas
del índice, es decir, las parejas de valor y RID. Además todos los valores de un
nodo hoja son más pequeños que los valores del nodo hoja siguiente y todos los
valores de algún nodo interno del árbol están repetidos en algunas de sus hojas,
así que las hojas contienen todos los valores del árbol.
Al suprimir un nodo hoja puede ocurrir que la hoja quede con menos
valores de los necesarios para cumplir los criterios de eficiencia, en este caso se
echará mano del nodo hermano de la derecha, y si no lo hay de la izquierda,
repartiéndose los valores y suprimiendo una entrada en el nodo padre; incluso
puede ocurrir que esto acarreé la necesidad de suprimir un nivel completo en el
árbol, lo cual sólo puede ayudar a ser más eficientes en las búsquedas.
Los índices que permiten implementar los accesos secuenciales por valor
(como los árboles B+) pueden ser índices agrupados o no agrupados. Un índice
agrupado es aquel en el que los datos que indexa están ordenados físicamente
según el acceso secuencial por valor que proporciona el índice. Para una misma
tabla podemos tener varios índices, pero solo uno de ellos puede ser agrupado. Por
ejemplo en la tabla empleados podemos indexar por el apellido. De forma que
cuando hagamos una búsqueda ordenada por apellidos, los RID que se obtendrán
apuntarán consecutivamente a filas contiguas, y así se podrán hacer muchos
accesos seguidos a filas de una misma página con una única entrada/salida.
Dispersión
Este factor es igual al número de entradas que se espera tener dividido por
el número de entradas que caben en las páginas primarias:
C = M / (N*L)
Cuanto más bajo sea el factor de carga (C), menos excedentes habrá y
menos entrada/salida serán necesarias; en contrapartida si el factor de carga es
muy bajo, habrá desaprovechamiento de espacio. Si el factor de carga es por
ejemplo C=0,5, se malgastará la mitad del espacio.
El sistema informix pone al alcance del diseñador los índices estudiados en este
tema; así para crear un índice con el valor del número de despacho:
Bases de datos 2 · 33
Finalmente recordar que estos índices tienen por defecto un orden ascendente, si
por el contrario se prefiere o necesita un orden descendente:
Una aplicación de acceso a bases de datos debe distribuir las funciones que Tema 7
Bases de datos distribuidas y
han de ejecutar los diferentes ordenadores que están conectados: cliente/servidor
1. Visión general del acceso
Almacenamiento de datos: Puede haber uno o más ordenadores con acceso a a bases de datos por red
2. Bases de datos cliente/servidor
los dispositivos físicos de almacenamiento. 3. Bases de datos distribuidas
Ejecución del código: Tanto el código de la aplicación con el de los SGBD, una 4. Ejemplo de aplicación
parte debe ejecutarse en el ordenador del cliente y otra en los servidores de cliente/servidor y distribuida
datos.
Almacenamiento del código: Normalmente se almacena el código necesario en
cada parte donde se ejecuta, sea el cliente o sea el servidor.
En una aplicación BD cliente servidor, hay una parte cliente que recibe las
peticiones de la aplicación y una parte servidor que lleva a cabo el acceso a los
datos; ambas partes se comunican por medio de la red como vemos en la figura
lateral.
Hay dos tipos básicos de arquitectura:
Hay 12 reglas fundamentales que debe cumplir Comunicación entre cliente y servidor
todo SGBD que trabaje con datos distribuidos:
Máxima autonomía local.
Igualdad entre servidores
Operación continuada
Transparencia de localización
Transparencia de fragmentación
Transparencia de replicación
Procesamiento distribuido de consultas
Gestión distribuida de transacciones
Independencia del hardware
Independencia del sistema operativo
Independencia de los protocolos de red
Independencia del SGBD
Arquitecturas
las relaciones entre los datos con lo cual el rendimiento y la integridad de los
datos es deficiente.
Capa intermedia de gestión de datos: Esta capa proporciona un acceso
más o menos integrado a la gestión de datos. Esta forma de trabajar suele ser
viable para aplicaciones de consulta con pocas exigencias de rendimiento e
integridad de los datos.
Distribución en el cliente: Es un acceso más integrado que los anteriores y
ofrece un rendimiento aceptable aunque si el cliente es ligero normalmente hay
que acceder a cada uno de los servidores por separado.
Distribución en el servidor: Es la solución más común y así se alcanza el
máximo grado de acoplamiento.
Esta arquitectura soporta la caída del servidor local de una delegación sin
que las demás se afecten (porque no usa replicación síncrona ni protocolo de
confirmación en dos fases), pero no así el servidor central que provocará una
parada general, por lo cual éste es más robusto y fiable que los de las
delegaciones.
Bases de datos 2 · 39
Podemos observar que cada estrategia tiene un coste diferente, por lo que
el orden de ejecución de las operaciones formuladas dentro de una consulta,
repercute directamente en el coste de ejecución de la consulta.
2. HERRAMIENTAS DE ADMINISTRACIÓN
Consisten en software que incorporan los SGBD para facilitar las tareas de
gestión y administración de la base de datos. Actualmente estas herramientas han
mejorado mucho, permitiendo una interfaz de usuarios y ventanas y paneles
desplegables que facilitan mucho la tarea del administrador de bases de datos.
Las herramientas que se encuentran por tanto en Informix y que nos ayudan a
realizar todas estas tareas son: Command Center, Schema Knowledge, Informix
Server Administrador, Control Center, SQL Enterprise Manager.