UNIVERSIDAD AUTONOMA METROPOLITANA UNIDAD: IZTAPALAPA DIVISION: CIENCIAS BASICAS E INGENIERIA CARRERA: INGENIERIA ELECTRONICA MATERIA: PROYECTO DE INGENIERIA ELECTRONICAI Y II TITULO: INTERFACES DE PROGRAMACION DE APLICACIONES PARA CONEXION A BASES DE DATOS. ODBC, UNA API DE VISUAL BASIC Y JDBC. FECHA: MARZO DE 1999 ALUMNO: GARCIA CUEVAS JUVENAL MATRICULA: 89327433 ASESOR: LUIS FERNANDO CASTRO CAREAGA Interfaces de Programaciónde Aplicaciones para conexión a bases de datos Interfaces de Programación de Aalicaciones aara conexión a bases de datos ODBC, una API de Visual Basic y JDBC Juvenal Garcia Cuevas División de Ciencias Básicas e Ingeniería Universidad Autónoma Metropolitana Unidad lztapalapa 1 Revisión Ing. Luis Fernando Castro Care Departamento de Computación Universidad Autónoma Metro Unidad lztapalapa Interfaces de Programación de Aplicaciones para conexión a bases de datos ODBC, una API de Visual Basicy JDBC Juvenal Garcia Cuevas El contenido de ésta obra corresponde a la documentación del proyecto de Ingeniería ElectrónicaI y II llevado a término por el autor, de acuerdo con el plan de estudios vigentede la Universidad Autónoma Metropolitana Impreso en México Esta obra se terminó de imprimir en Marro de1999 en las instalacionesdel Edificio T de Ciencias Básicas e Ingeniería de la Universidad Autónoma Metropolitana Unidad Iztapalapa, Av. Michoadn y la Purísima Col. Vicentina Delegación lztapalapa 09340 México, D.F. Se imprimieron3 ejemplares Para todas aquellas personas que de un modo u otro contribuyeron en la creación de éste trabajo ... ... ya sea en forma metafísica, mística, espiritual, activao cognoscitiva. Prólogo Se ha dicho que la necesidad es madre de la inventiva, y se hadichobien.Antesdequeyo siquiera pensaraen terminar mis estudiosen la UAM Iztapalapa,el profesor Luis Castrome invitó a participar en un proyecto avalado por la universidad. Este consistía en el desarrollo de sistemas para el corporativo PEMEX REFINACION, con sede en el puerto de Veracruz. Para mí era una excelente oportunidad de andar nuevamente en el camino de la Ingeniería de Software y de probar las habilidades adquiridas durante el ciclo universitario, pues antes de ello trabaje exclusivamente en el campo de las redes computacionales. Durante las primeras cinco semanas de desarrollo para el convenio PEMEX-UAM, huboel requisito de acceso a bases de datos por ODBC. En la primera semana me dediqué a investigar sobre el tema,desdesaberquesignificabaODBChasta la manerade utilizarlo. ODBCcuentacon un conjuntoespecíficodefuncionespararealizarlastareasdeconexiónydeejecuci6nde instruccionesdeSQL,peroesono estodo,algunasdelasfuncionesrequieren de unagran cantidad de argumentos, cada uno con un objetivo especial, inclusive se utilizan apuntadores. Esto no habría significadogran cosa de haberse usadoel lenguaje C, pero el problema radicaba en quelaherramientaescogidapara el desarrolloeraVisualBasic y sehabíandescartado los mecanismosdeaccesoadatosporRDO y DA0 por excederse en los tiempos de respuesta. Finalmente me dedique a experimentar el uso de las funciones de ODBC dentro de Visual Basic. Elaboré infinidad de pequehas pruebas para cada una de las funciones que a mi juicio serían las más útiles a nuestras necesidades. Poco a poco, las pruebas que había elaborado me revelaron patrones acentuados en cuanto a la forma de disponer de las funciones de ODBC y convine en aprovechar tal comportamiento parael beneficio general, creandouna lnterfaz de Programación de Visual Basic basada enODBC. Los resultados obtenidos fueron satisfactorios. Posterior al desarrollo en Visual Basic, surgió un nuevo desarrollo para Web, al igual con miras a consultarunabasededatos, de aquí mi conocimientosobreJDBC,puesen6saocasiónla plataforma fue Java. En esta obra, que presento como proyecto terminal, expongo las experiencias y conocimientos adquiridos en el campo de la conexión a bases de datos. Los temas se dividen en tres grupos a saber:ODBC, la lnterfaz de conexiónabases de datosparaVisual Basic-que hellamadola biblioteca de funciones- y finalmente, JDBC. Contenido Prólogo ¡x Introducción xv PARTE I. ODBC i I Aspectos generales de ODBC 3 1.1 La Conectividad a Bases de Datos Abiertas Porqué usar ODBC 1.2 Los Drivers y el Driver Managerde ODBC El Driver Manager de ODBC ODBC Desktop Database Drivers 1.3 Registro de una fuente de datos Registrando una fuente de datos en el código 2 La API de ODBC 9 2.1 El enfoque Cliente/Servidor 2.2 Objetos de Datos Remotos o llamadas a funciones de la API de ODBC 2.3 Codificación directa a la API deODBC 9 11 12 13 14 Arquitectura del modelo de codificación directa 2.4 La librería deenlace dinámico: ODBC32.DLL 3 Conexión ODBC a basesde datos 3.1 Primero: Se requiere un handle de ambiente La función SQLAllocEnv ( ) 3.2 Segundo: Se requiere un handle de conexión La función SQLAllocConnect ( ) 3.3 Tercero: Se procede a la conexión Requisitos para la conexión Creando el nombre de fuente de datos (DSN) de ODBC Tres maneras de culminar la conexión La función SQLConnect ( ) 3.4 Ultimo: Se liberan los recursos usados La función SQLDisconnect ( ) La función SQLFreeConnect ( ) La función SQLFreeEnv ( ) 3.5 Formato básico de unaaplicación ODBC xi IS 15 15 16 17 17 18 18 18 19 21 21 22 23 23 3.6 Conexiones múltiples y manejo de errores Manejo de errores La función SQLError ( ) Recuperando mensajesde error Mensajes de error de ODBC Formato del mensaje de error 3.7 Los códigos de retornoy los tipos de datos Los tipos de datos 4 El manejo de instrucciones de SQL 4.1 Lo primordial: El handle desentencia La función SQLAllocStmt ( ) 4.2 SQLExecDirect: Ejecutar instrucciones de SQL La función SQLExecDirect ( ) 4.3 SQLBindCol: Asociando variables y columnas El manejo de variables de Visual Basic y SQLBindCol Usando cadenas deVisual Basic con SQLBindCol Usando memoria global: funciones dela WinAPI La función SQLBindCol ( ) 4.4 SQLFetch: Navegar por los resultados La función SQLFetch ( ) 4.5 SQLGetData: Recuperando la informaciónpor columnas La función SQLGetData ( ) 4.6 Esquema general de procesamientode instrucciones de SQL 5 Resumen de funciones deODBC 5.1 Niveles de conformidad 5.2 Funciones y niveles de conformidad Funciones del núcleo Funciones del Nivel 1 Funciones del Nivel 2 Para más información 25 26 27 29 29 30 31 31 33 33 34 35 35 37 37 38 39 41 44 44 46 47 49 51 51 52 52 54 55 56 PARTE II. La biblioteca de funciones 57 6 Sobre la biblioteca de funciones 59 6.1 Objetivo y alcance de lasfunciones 6.2 Tipos de Datos Abstractos utilizados El tipo Base de Datos El tipo Registro de SQL El tipo Consulta de SQL Definiciones deconstantes para códigosde retorno 6.3 Llamadas a laAPI de ODBC involucradas 59 60 60 60 61 61 62 7 Conexion.bas: La biblioteca de funciones 7.1 La función ConectarBD 7.2 La función ConectarBD2 7.3 La función HacerConsultaSQL 7.4 La función HacerEscDirectaSQL La función HacerEscrituraSQL 7.5 La función EjecutarSQL 7.6 Las funciones de navegación de resultados 7.7 La función CerrarInstrucSQL 7.8 La función DesconectarBD 7.9 La función ErroresNativos 7.10 Las funciones privadas La función privada DescErrorODBC La función privada RepUltimaConsulta 65 65 66 68 69 70 70 71 72 73 74 74 75 75 77 8 Uso de las funciones 77 80 81 8.1 Un ejemplo sencillo 8.2 Esquema deuso de las funciones 8,3 Nuevamente el manejo de errores PARTE 111. JDBC 83 9 Introducción a JDBC 85 9.1 Que es JDBC Qué es lo que hace JDBC JDBC es una API de bajo nivel y es la base de APIs dealto nivel 9.2 JDBC versus ODBC y otras APIs 9.3 La arquitectura de JDBC Acceso de la API JDBC a las bases dedatos 10 Conexión JDBC a bases de datos 85 86 86 87 88 89 91 91 92 93 94 94 95 96 98 10.1 Las clases del paquete java.sq1 Interfaces de JDBC Objetos de JDBC Excepciones de JDBC 10.2 Conexión con la base de datos La interfaz Driver El objeto DriverManager Sobre los URLs de JDBC xiii 10.3 Creación y ejecución de sentencias de SQL La interfaz Connection La interfaz Statement El objeto Resultset Un ejemplo simple de ejecucibnde instrucciones de SQL 10.4 Las herramientas de JDK javac-El compilador deJava java-El Intkrprete de Java Compilando y corriendo un ejemplo Para más infomacibn 99 1O0 1O0 1o1 102 103 104 105 106 1o9 PARTE IV. Apéndices 111 A Declaración de las funciones de la API de ODBC. 113 B Conexion.bas: Código de las funciones. 133 Bibliografía 157 lndice 159 xiv Introducción La conexión a bases de datos abiertas es uno de los temas más importantes en la industria de la computación. La necesidad de comunicación con los distintos sistemas de bases de datos para el acopio de información ha dado paso a uno delos estándares más revolucionarios en el terreno de la informática: La Open Database Connectivity u ODBC, la cual es ahora una de las herramientas más utilizadas en el desarrollo de aplicaciones que necesitanel acceso a múltiples bases de datos con un alto grado de eficiencia y confiabilidad. Más recientemente, ha visto la luz el similar de ODBCpara el desarrollodeaplicacionesparaaccesoabasesdedatosen el Web: LaJava Database Connectivity o JDBC, que ofrece un cúmulo de posibilidades y que muchas firmas de software han comenzado a explotar. Este trabajo trata de la conexión a las bases de datos utilizando la API de ODBC y las clases de JDBC.SepresentatambiknunaAPIdeVisualBasicbasadaenODBC.LaAPI(Application Programming Interface, lnterfaz deprogramacidndeaplicaciones)deODBC,es el conjuntode funcionesde la Librería deEnlace D i n h i c o ODBC32.DLLparalaconexiónabasesdedatos abiertas (Open Database Connectivity, Conectividad a bases de datos abiertas) mediante la cual es posible acceder a cualquier base de datossin importar cual sea. La conexión a bases de datos desde Java se logra con JDBC (Java Database Connectivity, Conectividad a bases de datos de Java), la cual es tambien proporcionada por las clases recientemente incorporadas al JDK en su versi6n 1.Ipor Sun Microsystems. La información contenida aquí sedivide en tres partes. En la primera se realiza un breve estudio de ODBC y como es que permite hacer conexiones a bases de datos múltiples, se hace tambi6n una descripcidn de la API y de las llamadas a 6sta para usar sus recursos. En la segunda parte se estudian las funciones basadas en ODBC construidas en Visual Basic y la forma de explotarlas. Finalmente, en la tercera parte, se revisa la forma de usar la interfaz de JDBC para la conexión a o desde la Internet. bases de datos desde Java dentro de una intranet. xv PARTE I. ODBC O Aspectos generales de ODBC O La API de ODBC O Conexión ODBC a bases de datos O El manejo de instrucciones de SQL O Resumen de funciones de ODBC 1 Aspectos generales de ODBC Para hacer uso de la conectividad a bases de datos abiertas, es necesario contar con los drivers adecuados de ODBC de las bases de datos que se deseen accesar, as¡ como del administrador de fuentesdedatosdeODBCparadisponer de lasbases de datos. En &te capítuloseda una introducción sobre ODBC y se describen los controladores o Drivers que componen la interfaz de ODBC y el uso del administrador de fuentesde datos ODBC. 1.1 La Conectividad a Bases de Datos Abiertas ODBC son las siglas para Open Database Connectivity (conexión a bases de datos abiertas), la cual es una especificación de Microsoft. ODBC fue creado por el SQL Access Group y su primera liberaciónfueenSeptiembrede1992. AI mismotiempoMicrosoftWindowsfue el primero en UNIX, OS/2,y proporcionarunproductoODBC,ahora ya existenversionesparaplataformas Macintosh. ODBC es unestandar o una lnterfaz de Programacidn de Aplicaciones(API,Application ProgrammingInterface)para el accesoalasbasesdedatos.EstaAPIesindependiente de cualquiersistemamanejadordebases de datos o sistemaoperativo;laAPIdeODBCes independiente del lenguajede programación. LaAPI de ODBC esta basada en las especificaciones CLIdeX/OpeneISO/IEC.ODBC 3.0 implementa ambasespecificaciones-lasversiones anteriores de ODBC se basaron en las versiones preliminares de 6stas especificaciones pero no las implementaron-y adicionaron características requeridas por los desarrolladores de aplicaciones de bases de datos basadasen pantallas, tales comolos cursores navegables. AI usar llamadas a funciones ODBC en un programa se pueden acceder a archivos de varias y diferentesbases de datos,incluyendoAccess,dBase,Excel y Texto. AdemAs del softwarede 3 4 ODBC, es necesario un controlador o driver para cada base de datos que es accesada. Microsoft es quien principalmente proponey proporciona el soporte a la programaci6n con ODBC. ODBC está basado y estrechamente alineado al Open Group Standard Structured Query Language (SQL) Call-Level Interface (CLI). Esto permite a los programas usar consultas SQL que accesarán alasbasesdedatos sin tenerquepreocuparsede las interfacespropietariasdelasmismas. ODBC maneja las consultas SQL y las convierte en consultas interpretables por cada sistema de base de datos individual. Porqué usar ODBC ODBC proporciona una manera uniforme de definir fuentes de datosy sus respectivos metodos de acceso a las mismas. ODBC está disefiado basándose en SQL y las bases de datos relacionales. La especificación de ODBC define llamadas de nivel bajo a la API que cualquier aplicaci6n puede usar para consultar una base de datos. AI escribir llamadas a la API una aplicaci6n cualquiera puede accesar fuentes de datos heterogeneas con s610 un código fuente. ODBC permite a las aplicaciones el acceso a los datos en una forma común, la cual les da a las aplicaciones portabilidad, agiliza su desarrollo y facilita la curva de aprendizaje de los desarrolladores cuando se requieren múltiples fuentes de datos. ODBC está reconocido como un estándar en el acceso de datos. 1.2 Los Drivers y el Driver Manager de ODBC ODBC estádiseñadoparaunmáximainteroperabilidad,estoes,lahabilidaddeunaaplicaci6n para accesar diferentes sistemas manejadores de bases de datos (DBMS) con el mismo cbdigo fuente. Las aplicaciones de bases de datos llaman a las funciones en la interfaz de ODBC, la cual está implantada en m6dulos propietarios de la base de datos específica llamados controladores (en ingles Drivers). El uso de controladores aísla a las aplicaciones de las llamadas específicas de una basededatosenlamismaformaenque los controladoresdelasimpresoras aislan a los programasprocesadoresdetextosde los comandosespecíficosdelasimpresoras.Como los controladores son cargados en tiempo de ejecucidn, un usuario s610 tiene que agregar un nuevo controlador para accesar a un nuevo DBMS; no es necesario recompilar o reenlazar la aplicaci6n. Las funciones en la API de ODBC son implementadas porlos desarrolladores de los controladores deDBMSespecíficos.Lasaplicacionesllamanalasfuncionesenesoscontroladorespara el acceso a datos en una forma independiente al DBMS. Un administrador de controladores maneja la comunicaci6n entrelas aplicaciones y los controladores. AunqueMicrosoftproporcionaunadministradordecontroladores (Driver Manager)paralas computadoras que corren Windows NT Server, Windows NT Workstation, y Windows 95; se han escrito varios controladores de ODBC, así comolas llamadas a las funciones de ODBC de algunas de sus aplicaciones; cualquiera puede escribir aplicaciones y controladores de ODBC. De hecho, la mayoría de las aplicaciones y controladores de ODBC disponibles para computadoras que corren los sistemas antes mencionados son producidos por otras compafiías además de Microsoft, los controladores y las aplicaciones de ODBC existen tambien para Macintoshy una gran variedad de plataformas UNIX. 5 Paraayudara los desarrolladores de controladores y aplicaciones, Microsoft ofrece el Kit para Desarrollo de Software ODBC (SDK ODBC) para computadoras con Windows NT Server, Windows NT Workstation, y Windows95, el cual proporciona el DriverManager, el instaladordeDLL, herramientas para pruebas, y aplicaciones de ejemplo. Es importante entender que ODBC este disefiado para explotar las capacidades de las bases de datos, no para sustituirlas. Por esto, los escritores de aplicaciones no deben esperar que usando ODBC repentinamente transformart!tn una simple base de datos en un sistema completo de base de datos relacional. Tampoco hay escritores de controladores que implementen funcionalidades que no hay en las bases de datos conocidas. Una excepcibn a esto es que los desarrolladores quienes escriben controladores que directamente accesan archivos de datos (tales como los datos en un archivo Xbase) requieren escribir un sistemade base de datos que soportela funcionalidad mínima de SQL. Otra excepcibn es el ODBC SDK, el cual proporciona una librería que simula los cursoresnavegablesparacontroladoresqueimplementenuncierto nivel de funcionalidad. Las aplicaciones que usan ODBC son responsables de cualquier funcionalidad que implique basesde datoscruzadas. Porejemplo,ODBCnoesunsistema de unionesheterogéneas,niesun procesador de transacciones distribuidas. Sin embargo, como es independiente del DBMS, puede ser usado para construir tales herramientas de bases de datos cruzadas. El Driver Manager de ODBC El administrador de controladores de ODBC u ODBC Driver Manager, proporciona la interfaz entre el host y el back-end del controlador de la fuente de datos. El administrador de controladores es responsable de: Cargar el controlador de la base de datos remota especificado por la entrada de nombre de fuente de datos. lnicializarlainterfaz. Proporcionar puntos de entrada a los correspondientes del controlador. Validar paremetros y manejar la serializacibn de funciones de ODBC. El administrador de controladores de ODBCesuna Librería de Enlace Dinemico (dynamic link library, DLL)-en particular se trata del archivoODBC32.DLL-que puede hacer de interfaz con un back-end (servidor) de un sistema de base de datos o un tipo específico de archivo implementado como una basede datos. Hay otros controladores especiales para cada tipo de base de datos, que conectan al Microsoft SQL Server, a bases de datos Sybase SQLServer, Oracleo Microsoft Visual FoxPro. Siempreque esposible, el controladortomaventajadelascaracterísticasback-endespecíficas,como los cursores o lasconsultasparametrizadas. En algunoscasos, el controladoren sí cuentacon funciones específicas para suplir las características no soportadas por el back-end de la base de datos. Si el controlador no soporta la caracteristica requerida, éste regresa un error indicando que es incapaz de realizarla operacibn. ODBC Desktop Database Drivers Los ODBC Desktop Database Drivers de Microsoft permiten abrir y consultar las bases de datos a través de la interfaz de ODBC. Estos controladores trabajan con ODBC 2.5, pero se incluyen con ODBC 3.0. Los controladores estan disenados para su uso con Microsoft Windows 95 (o posterior), 6 o Windows NT 3.51 (o posterior). S610 se soportan aplicaciones de 32 Windows NT se soportan aplicaciones de16 bits y de 32 bits. bits en Windows 95; en Los ODBC Desktop Database Drivers incluyen controladores de 32 bits para Microsoft Access, dBase, Microsoft Excel, Microsoft FoxPro, Paradox y Texto (Un controlador para Microsoft FoxPro 3.0 esta disponible por separado). No se tienen controladores de 16 bits. La Figura 1 muestra una lista de los controladores que estan disponibles para Windows. Cualquier otro controlador para cualquier otra base de datos que se desee agregar es necesario adquirirlo por separado con la compañía que fabric6la base de datos. 3.50.360... Microsoft Corp... f) 3.50.360...Microsoft Corp... MicrosoftODBCDriver for Oracle2.00.006 ... Microsoft Corp... b ) 3.50.%O... Microsoft Corp... csv) 3.50.360... Microsoft Corp... odbcit32.dll odbcjt32.dll MSORCL1O.DLL odbcit32.d odbcit32.dll 2.00.0301 Oracle Corpor... sqo32-73.d 2.65.0240 Microsoft Corp... sqlsrv32.dll Figura l.ODBC Desktop Database Drivers. 1.3 Registro de una fuente de datos El primer paso en el acceso a datos por ODBC es utilizar el manejador de fuentes de datos de ODBC pararegistrar la fuentededatos.Estepasoesimportante ya queasísealmacenala inforrnacidn de la fuente de datos en el Registro de Windows y hace esta informaci6n disponible para las aplicaciones. Para dar de alta una fuente de datos: 1) En el Panel de Control de Windows, dar doble-click enel icono 32bit ODBC. 2)Darclicken Agregar... paradesplegar el dialogo Crearnuevafuentededatos, luego se selecciona el controlador ODBC para la fuentede datos que se desee accesar. Por ejemplo se puede seleccionar Microsoft SQL Server. 7 En el cuadro de Configuraci6n, teclear el nombre de la fuente de datos o Data Source Name (DSN). Este puede ser cualquier cadena, como "VentasDB" o "Pubs". La cadena no tiene que corresponder con el nombre actualde la base de datoso tabla quese desea accesar. Opcionalmente, teclear una descripción de la base de datos, como "Datos deVentaspara 1998.Se puede teclear cualquier texto. Teclear el nombre de red del servidor en el cual reside la fuente de datos. No alladir el doblebackslash ("\\") para comenzar el nombre. Teclear el nombre de la base de datos que se accesara.Por ejemplo, para especificar la base de datosde ejemplo deMicrosoft SQL Server, teclear "Pubs". La configuración para cada fuente de datos ODBC puede variar debido a que cada controlador requiere de un conjunto diferente de información. Si el dialogo de configuración de la fuente de datos que se esta agregando tiene valores no descritos en los pasos mostrados arriba, haga click en el botón de ayuda de la caja de dialogo para mas información. Registrando una fuente de datos en el código En algunos casos es deseable registrar la fuente de datos dentro del c6digo en lugar de dejarles a los usuarios el trabajo de configurarla con el manejador de fuentes de datos. Para hacer esto, se utiliza el m6todo RegisterDatabasedel objeto DBEngine en Visual Basic5.0. El procedimiento mostrado enel Listado 1 registra una fuentede datos llamada "Cuentas" para una base de datos del mismo nombreen el servidor "ServCuentas". Listado 1. Registro de una fuentede datos en Visual Basic. Sub RegisterDB () Dim str As String ' Construye la cadenade información. str = "Description=SQL Server en Servidor ServCuentas" & vbCr & "OemToAnsi=No" & vbCr & "Network=(Default)" & vbCr & "Address=(Default)" & vbCr & "Server=ServCuentas" & vbCr & "Database=Cuentas" - ' Registra la base de datos DBEngine .RegisterDatabase "Cuentas", "SQL Server", True, str End Sub Cabemencionarquedependiendo de la basededatos, los valoresque se incorporaranala cadena de información seran diferentesen cada caso. El listado anterior es parael caso específico del manejador de bases de datos de Microsoft SQL Server. 2 La API de ODBC La lnterfaz de Programación de Aplicaciones (API) de la Conectividad de Bases de Datos Abiertas (ODBC) define un modelo de programación independiente de la base de datos que proporciona una sola interfaz que se puede usar para accesar cualquier base de datos o servidor de base de datosquetengauncontroladorODBC.AIusarla API deODBC,sepuedendesarrollar aplicaciones que tengan el rendimiento y flexibilidad de los modelos de programación de código nativo. AI mismo tiempo, es posible mantener una aproximación general de programación universal que puede ser aplicado a diversos formatos de bases de datos. Este capítulo da una breve descripción de la API de ODBC para tomar una decisión adecuada sobre el modelo de programación a utilizar en un aplicación ClientelServidor. 2.1 El enfoque ClientelServidor La API de ODBC es una interfaz de nivel de llamada (Call-Level Interface, CLI) alos controladores y libreríasdeODBC.Estaslibreríasproporcionanconectividadparaaccesoadatoshacia el Microsoft SQL Server y hacia cualquier otra basede datos que proporcioneun controlador ODBC. AI codificar con esta interfaz, se puede crear código independiente de la base de datos, lo cual significa que la API de ODBC proporciona un modelo de programación universal que automaticamente se adapta a una gran variedad de bases de datos. La API de ODBC a menudo es extendida con funciones especialesdel controlador en sí-ademas de las que pertenecen al manejador del controlador ODBC. Por ejemplo, el controlador del SQL Server contiene varias funcionesde su API las cuales no estan documentadas fuera del ambit0 del controlador. Ademas, esas funciones realizan tareas específicasdel controlador, su uso podría no ser soportado en otros controladores. Si una aplicación esta pensada para ser utilizada dentro de 9 10 una amplia variedad de sistemas back-end, se deben usar esas funciones con sumo cuidado o no utilizar todas las que hay. LaAPIdeODBCproporcionaunextensocontrolsobre los errores y los mensajesypermite manejarla mayoria de las características de los servidores de back-end. La API de ODBCno implementa el ligadodecontrolescondatos,pero sí cuentaconsuspropiosconjuntos de resultados que utilizan cursores tipo client-side o tipo server-side. El modelo de programacidnde la API de ODBC proporciona las siguientes características: Un modelo de programaci6n universal que puede accesar ala mayoría de las arquitecturas de bases de datos. Una interfaz nativa con Microsoft SQL Server. Un manejo inteligente de parametros de Stored Procedures. 6.0. Soporte a cursores server-side en Microsoft SQL Server versidn Implernentacion de cursores keyset, dynamic, static y forward-only. Manejo avanzadode conjuntos de resultados. Acceso a virtualmente todas las características de SQL Server. Manejo completode errores post-llamada. La Figura 2 ilustra como es queVisualBasicejecutac6digo de la APIdeODBC, el cual directamentemanipula los puntosdeacceso de la libreríadelmanejadordecontroladores de ODBC. Estos puntos de accesoseconectan al controladorquepermite el accesoa un tipo específicodebase de datosremotaeimplementalasoperaciones de accesoa los datos. Usualmente no es necesario reescribir la aplicaci6n para acceder a un tipo diferente de base de datos. Todo lo que se necesita es cambiar el controlador de la base de datos remota. I Código Visual Basic ODBC Driver Manager Driver Server Driver U Oracle Remote Database Engine t Figura 2. Acceso a basesde datos remotas usando la API de ODBC. 11 2.2 Objetos de Datos Remotos o llamadas a funciones de la API de ODBC La API de ODBC ha proporcionado una interfaz confiable a los desarrolladores de front-end de y relativamente estable con lacual conectarse bases de datos que necesitan una plataforma rdpida a los sistemasClientelServidor.LaAPIde ODBC estádisefiadaparacorrerendiversas plataformas, incluyendo Windows de16 bits, Windows 95 y Windows NT de 32 bits, y varios otros. Sinembargo, como lacomplejidad y laflexibilidad de dstasplataformasaevolucionado,la complejidad para usar el modelo de programaci6n de laAPI de ODBC en Visual Basic tambien se ha incrementado. Aunque es posible crear aplicaciones usando la API de ODBC desde Visual Basic, dsta no proporciona tantos beneficios comoel uso de la interfaz Remote Data Object u otras interfaces de modelo-objeto-especialmente en plataformas de 32 bits. La conversión de aplicaciones ODBC API de 16 bits a 32 bits puede ser especialmente problemdtica cuando se tienen nuevos manejos de memoria y características Unicode,y otros requerimientos de c6digo de mayor complejidad. En general, existen dos aproximacionesal uso de la API de ODBC: Extensidn de RDO con la API de ODBC. Debido a que los Objetos de Datos Remotos (RDO) son, en su mayor parte, interfaces de objetos a la API de ODBC, tiene sentido utilizar las funciones de la API de ODBC en conjuncibn con una aplicacidn RDO u ODBCDirect. Usar los handles de ODBC proporcionados por las interfaces de nivel-objeto para llamar a las funciones de la API de ODBCpuedeserunaefectivaeinteligenteformadecontrolar o rdoResultset. aspectos de los objetos rdoEnvironment, rdoConnection, rdoQuery Nota: Si se hace mal uso de ciertas funciones de la API de ODBC con handles dados por o perdida de datos. RDO, se corre el riesgo de fallas de protecci6n general inesperadas Codificaci6nDirecta a la API deODBC. Es posible crear una aplicacidn usando la API de ODBC que nodependa de RDOuotrainterfaztipoobjetoparaobtenerhandles,abrir conexiones, crear y manejar cursores, o realizar operaciones complejas de recuperacidn de datos, enlazado y de actualizaci6n. Todas dstas funciones pueden ser realizadas por laAPI de ODBC. Aunque se gana algún grado adicional de rapidez, flexibilidad y memoria con el uso directo de la API de ODBC, se tiene un impacto negativo en el tamano del programa y en su rendimiento, adem& de quees mucho rndsdifícil de codificar y de mantener. Nota: Algunos desarrolladores escogen la API de ODBC para accesar a las bases de datos de Microsoft Jet. Aunque es posible usar los controladores de ODBC proporcionados con algunas aplicaciones de Microsoft Office, dsta aproximaci6n requiere de la Jet database engine y siempre resulta en un pobre rendimientoal compararse conel uso de los Objetos de Acceso a Datos (DAO) para accesar este tipode base de datos. Aun más, muchas de las características mas significativas de la Jet database no son aprovechadas por los limitados controladores Jet ODBC, de forma que se podríanexperimentaralgunasdificultades al realizarlamayoríadelasoperacionesbdsicas mediante dsta aproximaci6n. Cadaunode los dos modelos arriba mencionados tiene sus ventajas y desventajas. Sedeben considerar todos los aspectos antes de tomar una decisi6n sobre el modelo de programación a utilizar. Sin embargo, el modelo que nos ocupa es el de la codificacidn directa a la API de ODBC, que implica las llamadas a las funciones de la librería de enlazado dinAmico ODBC32.DLL. 12 2.3 Codificación directa ala API de ODBC Se puede codificar directamente a la API de ODBC. Este modelo de programacidn utilizael mismo administrador de controladores y controlador de ODBC específico que Jet usa. Sin embargo, es responsabilidad del programador manejar todos los aspectos de la conexidn, consultas, búffers, cursores y cualquier error quesea generado, dentro del cddigo de Visual Basic. La Figura 3 ilustra cdmo la declaracidn de sentencias de la API permite la comunicacidn con el administrador de controladores de ODBC para acceder a datos en una base de datos remota. Visual Basic + * * MI Declare Statments ODBC Driver I ODBC Driver 1 + Network Interface I I + Remote Database Engine I Figura 3. El modelo de programación de la API de ODBC en Visual Basic. El modelo de programacidn de la API de ODBC utiliza instrucciones Declare de Visual Basic, el administrador de controladores de ODBC y un controlador específico para la base de datos o servidor. La parte de declaraci6n de funciones de la API ODBC incorpora funciones de la interfaz de ODBC a Visual Basic y a los procedimientos que necesitan accesar la API. De esta forma las aplicaciones de Visual Basic pueden usar las funciones de la API como cualquier otra funcidn. La manera de hacer esto es la siguiente: Declare Function Beep Lib "kerne132" Alias "Beep" (ByVal dwFreq As -Long, ByVal dwDuration As Long) As Long Declare Function BringWindowToTop Lib "user32" Alias "BringWindowToTop" (ByVal hwnd As Long) As Long Ambas declaraciones son para funciones de la API Win32 de Windows. En la primera se hace referencia a la funcidn Beep ubicada en la DLL kerne132, la cual provoca que el sistema emita el familiar sonido de acuerdo a los pardmetros de frecuencia y duracidn que le son proporcionados como argumentos. Y la segunda: BringWindowToTop, ubicada enlaDLLuser32,esparahacer que la ventana, cuyo handle se pasa como argumento, aparezca en primer plano. 13 Arquitectura del modelo de codificación directa La arquitecturade Aplicación/Controlador en Windows 95 o posterior es: Aplicación (32-bit) I ODBC Driver Manager (ODBC32.dll) I ODBC Desktop Database Driver (ODBCJT32.dl1, MSJET35.dI1, 32-bit Driver ISAM) I base de datos El uso de &tos controladores para aplicaciones de 16 bits en Windows 95 no esta permitido. La arquitectura de AplicaciÓnlControlador en Windows NT 3.51 o posterior es: Aplicación (16-bit) Aplicación (32-bit) I I I I I I ODBC Driver Manager (0DBC.dll) 16-Bit ODBC Generic Thunking DLL (ODBC16GT.dll) 32-Bit ODBC Generic Thunking DLL (ODBC32GT.dll) I I ODBC Driver Manager (ODBC32.dll) I I ODBC Desktop Database Driver (ODBCJT32.dl1, MSJET35.dl1, 32-bit Driver ISAM) I base de datos Nóteseque en el casodeWindows NT senecesitandedoslibreríasadicionalesparalas aplicaciones de 16 bits. Estas librerías funcionan como enlace entre los Drivers de 32 bits de las bases de datosy la vieja versión delODBC Driver Manager de 16 bits. 14 2.4 La librería de enlace dinámico: ODBC32.DLL Una librería de enlace dinamico (en inglks, dynamic link library, DLL) es un conjunto de programas o funciones previamente compiladas que son enlazadas en tiempo de ejecucibn por algún otro programa que las necesite para ejecutarse. Estas librerías cuentan con una tabla de exportacidn, la cual indica las funciones que estan disponibles para uso público. Ademas, pueden contaro no con una tabla deimportacibn,con la quehacenreferenciaaotras librerías quenecesitanparasu propiaejecucibn.LaAPIdeODBCestasustentadaprimordialmenteporlalibreríadeenlace dinámico ODBC32.DLL, pues es en 6ste archivo donde residen-o se importan de otros drivers-la totalidad de las funciones de ODBC que vamos a necesitar para la conexidn y la manipulacibn de datos. Si bien se requieren de los ODBC Desktop Drivers o del Driver de la base de datos dada para realizar la conexibn, no es conveniente hacer llamadas a las funciones de &tos Drivers, ya que algunos incorporan sus propias funciones de ODBC y otras especiales. Si se planea hacer uso de las funciones de &tos Drivers conviene hacerlo con sumo cuidado por razones de portabilidad como se vio en la primera parte de 6ste capítulo. Vamos a hacer un brevísimo analisis dela librería de ODBC, para ello nos valdremos del programa Quikview deWindows.Primero,busquemos el archivoODBC32.DLL,ubicadoen el directorio C:\Windows\Systern\ paraWindows 95 o en C:\Winnt\System32\ paraWindows NT. Unavez localizado el archivo hagamode un click con el botón derecho del Mouse y seleccionemos Vista @ida o Quikview según sea el caso. Veremos desplegarse una ventana con informacidn sobre el archivo seleccionado; pongamos particular atencidn en la parte designada como EXPORT TABLE, en6stasecciónseencuentrauna lista de las funcionesquehansidoexportadasparasuuso público, 6sta lista cuenta con tres columnas: Ordinal, Entry Point y Name. El primero es el número de la funcibn dentro del c6digo de la librería y corresponde a su posicidn ordinal, es decir, si es la primera,lasegunda,latercera,etc., los númerosde6stacolumnaestanenhexadecimal.La segunda columna corresponde a su mapeo en memoria una vez cargada la librería; por lo general unaDLLcontieneunvalorhexadecimalqueindicaladireccibndememoriapreferidaparaser cargada,comosepuedeveren el parámetro Base of Code, quetambi6nsemuestraenel Quikview del archivo, pero, sin embargo, es posible que otra DLL se cargue primero en esa base de memoria y por lo tanto nuestra DLL no pueda cargarse en su direccibn base predeterminada, por Io que se requiere que la DLL sea Redireccionable (Relocatable), y en efecto lo es, lo cual significa que si no puede cargarse en su base predeterminada puede redireccionarse en otra base de memoria y todos los mapeos de memoria de las funciones cambiaran de acuerdo a la nueva base. La tercera columna corresponde al nombre de la funcibn que es como debe importarse o llamarse en el cbdigo que las utiliza. Algunas de las funciones m& importantes que pueden verse aquí son por ejemplo: SQLAIlocConnect, SQLAllocEnv, SQLAllocStmt, entre otras; &stas y algunas más, serán estudiadas a fondo enlos capítulos siguientes. En el Ap6ndice A se muestra el listado de cbdigo en el que se declaranlas funciones de la librería de ODBC para su uso con Visual Basic. En 61 se pueden ver los argumentos que requieren cada una de ellas, así como los valores que regresan estas funciones. 3 Conexión ODBC a bases de datos Ya hemosvistocualeslanaturaleza del ODBC,laquepuederesumirseenunconjuntode funcionesencapsuladasenunalibreríadeenlacedinsmicollamadaODBC32.DLL.Enéste capítulo entraremos en detalle en el uso de las llamadas a la API relacionadas con la conexion a las bases de datos,el manejo de erroresy la creación de conexiones múltiples. 3.1 Primero: Se requiere un handle de ambiente Antesdequeunaaplicaciónpuedautilizarcualquierade las funcionesODBC, éSta debede inicializar la interfaz de ODBC y obtener un handle de ambiente con la funcibn SQLAllocEnv: Dim rc as integer, henv as Long rc = SQLAllocEnv(henv) Si SQLAllocEnv regresaSQL-SUCCESS, el ODBCdrivermanagerestainicializado, y seha reservado espacio en memoria para almacenar informacion acerca del ambiente. Si por el contrario la función falla, los DLLs de ODBC no se inicializaron correctamente. Solo se necesita ejecutar SQLAllocEnv unavezyaque el handledeambientequeseobtieneescapazdesoportar conexiones múltiples a fuentes de datos. La función SQLAllocEnv () Propósito SQLAllocEnv() coloca en memoria un handle de ambientey los recursos asociados para éste. Una aplicacion debe de llamar primero a dsta funcidn antes que a SQLAllocConnect() o que cualquier 15 16 otra funci6n de ODBC CLI. El valor henv es pasado despues a todas las funciones que requieran un handle de ambiente como entrada. Sintaxis SQLRETURN SQLAllocEnv (SQLHENV *phenv); Argumentos de la función Tabla 1. Argumentos de SQLAllocEnv. Argumento Tipo dato de Uso ~~~ Descripci&n ~ SQLHENV Phenv Apuntador al Salida ambiente Comentarios Sólo puedehaberunambienteactivoaunmismotiempopor aplicaci6n. Cualquierllamada posterior a SQLAllocEnv() regresael handle de ambiente existente. Se debe llamar a SQLFreeEnvO por cada SQLAllocEnv() que se haya ejecutado con Bxito para liberar los recursos asociados al handle. Códigos de retorno SQL-SUCCESS SQL-ERROR Si se regresa SQL-ERROR y phenv es igual a SQL-NULL-HENV, entonces no se puede llamar a SQLErrorO porque no hay un handle conel cual asociar informaci6n de diagn6stico adicional. Si el c6digoderetornoes SQL-ERROR y el apuntador al handledeambientenoesiguala SQL-NULL-HENV,entonces el handleesun handlerestringido. Estosignificaque el handle o solamente puede ser usado en una llamada a SQLErrorO para obtener mas informaci6n del error, por SQLFreeEnvO. 3.2 Segundo: Se requiere un handle de conexión Antes de que una aplicacidn pueda conectarse a un controlador, Bsta debe obtener un handle para laconexión.Lafunci6n SQLAllocConnect seusaparaesteprop6sit0,comosemuestraen el siguiente ejemplo: Dim rc as Integer, hdbc as Long rc = SQLAllocConnect(ByVal henv, hdbc) Si lafunci6n SQLAllocConnect regresaSQL-SUCCESS,se tiene memoriareservadaparaun handle de conexi6n. Si esta funci6n falla, se puede usar la funci6n SQLError para determinar la causa. Se debe establecer un handle de conexi6n para cada conexidn que se abra. 17 La función SQLAllocConnect () Propósito SQLAllocConnect() coloca en memoria un handle de conexibn y los recursos asociados para &te dentro del ambiente identificado porel handle de ambiente de entrada. SQLGetlnfo() conf/nfoType puesto a SQL-ACTIVE-CONNECTIONS, proporciona el número de conexiones que pueden ser almacenadas a un mismo tiempo. Se debe llamar a SQLAllocEnv() antes que &aa función. Sintaxis SQLRETURN SQLAllocConnect (SQLHENV henv, SQLHDBC *phdbc); Argumentos de la función Tabla 2. Argumentos de SQLAllocConnect. Tipo de dato Entrada SQLHENV Salida * SQLHDBC Argumento uso Descripción Henv Handle de ambiente Phdbc Apuntador a la conexi6n Comentarios El handle de conexibn de salida es usado por el ODBC CLI para referenciar a toda la informacibn relacionada con la conexibn, incluyendo informacibn del status general de la misma, estado de la transacción, e informacibn delos errores. Si el apuntador al handle de conexibn(phdbc)apunta a un handle de conexibn valido obtenido por SQLA//ocConnect(),el valor original se sobreescribe en cada llamada la a funcibn. Códigos de retorno 0 SQL-SUCCESS SQL-ERROR 0 SQL-INVALID-HANDLE Si se regresa SQL-ERROR, el argumento phdbc es puesto a SQL-NULL-HDBC. La aplicacibn debeentoncesllamaralafuncibn SQL€rror() con el handledeambiente (henv) y con los argumentos hdbc y hstmt puestos a SQL-NULL-HDBC y SQL-NULL-HSTMT respectivamente, para el chequeo de errores. 3.3 Tercero: Se procede a la conexión Una vez que los handles de ambiente y de conexibn han sido obtenidos,el cbdigo puede abrir una o mds conexiones a bases de datos remotas usando un handle de conexibn diferente para cada conexibn. AI trabajar con algunas bases de datos, puede ser necesario abrir mas de una conexibn para soportar operaciones simultdneas de consultas múltiples o actualizacibn-pero no todas las 18 bases de datos requieren de conexiones adicionales para operaciones múltiples. Es mejor crear varios handles de sentenciao instrucci6n enuna sola conexibn que crear múltiples conexiones con una solasentencia.Paradeterminar si el controladordelabasededatossoportamúltiples sentencias activas en una sola conexi6n se utiliza la funci6n SQLGetlnfo. Requisitos para la conexión El c6digo debe proveerlos siguientes argumentos para establecer una conexi6n: 0 0 Un handle de ambiente vdlido creado por SQLAllocEnv. Un handle de conexi6n vdlido creado por SQLAllocConnect. Un nombre de fuente de datos registrado que corresponde a la entrada DSN en el archivo 0DBC.ini o en el registro del sistema. Un ID de usuario vdlido (opcional), el cual es el ID de acceso o nombre de la cuenta usado para acceder a la fuente de datos. Un password o contrasetia valida (opcional) que correspondeal ID del usuario. Cualesquiera otros parametros opcionales que proporcionen informaci6n la cual el controlador seleccionarduna base de datos específica. al controlador, con Creando el nombre de fuente de datos(DSN) de ODBC Cuando se abre una conexi6n a cualquier base de datos o servidor de base de datos remoto con la API de ODBC, se hace referencia a un nombre de fuente de datos o Data Source Name (DSN). Aunque es posible crear DSNs en el c6digo con la funci6n de ODBC ConfigDSN, la forma mas sencilla de crear o modificar un DSN es usandoel programa Administrador del ODBCdel Panel de Control. La informaci6n del DSN se almacena en el archivo 0DBC.ini en los sistemas de 16 bits y en el registro del sistema en los sistemas de 32 bits. Si se tienen aplicaciones tanto de 16 bits comode 32 bits corriendo en un sistema operativo de32bits,talcomoWindowsNT o Windows 95, se deben establecer las entradas para el DSN para ambos casos, para el archivo 0DBC.ini y para el registro del sistema. Tres maneras de culminarla conexión Para establecer la conexibn, se pueden usar una de las tres funciones de la API de ODBC para pasar los parametros necesariosal manejador de controladores ODBC: SQLConnect acepta un nombre de fuente de datos DSN, ID del usuario, y password como argumentos. SQLConnect cambia el estado de hDbc conectado a y regresa SQL-SUCCESS, SQL-SUCCESS-WITH-INFO o SQL-ERROR. SQLDriverConnect acepta una cadena de conexidn similar ala de la propiedad Connect del control Visual Basic Data, excepto que no empieza conel pardmetro "ODBC;", y no soporta el pardmetro"TIMEOUT".Paraestablecer el timeoutde la conexi6n ODBC seutilizala funcidn SQLSetConnectionOption. Si no se da suficiente informacidn con la cadena, uno o mas didlogos se despliegan para obtener los pardrnetros faltantes o invdlidos. Esta funci6n 19 e regresa SQL-ERROR si el usuario hace click en el botón Cancelar o si el manejador no se puede conectar por otras razones. SQLBrowseConnect soporta un metodo iterativo para determinar los controladores disponibles, fuentes de datos y otros pardmetros. La aplicación escoger& de las fuentes de datos disponibles. En éste trabajo sólo se describird a la función SQLConnect por ser la mas sencilla de todas y porque es una de las funcionesdel núcleo primario de funciones de la API de ODBC. Las otras dos funciones: SQLDriverConnect y SQLBrowseConnect, sonextensionesde nivel 1 o nivel 2 al WOpen and SQL Access Group Call Level lnterface y no siempre son soportadas por todos los drivers.Paradeterminar si undriversoportaunafunciónespecífica,seutiliza la función SQLGetFunctions. La función SQLConnect () Propósito SQLConnectO estableceunaconexióna una basededatosdestino.Laaplicacióndebe proporcionar una base de datos destino, y opcionalmente un nombre de usuario y una cadena de autentificación o password correspondiente al nombre de usuario. Se debe llamar a SQLAllocConnect() antes que a ésta función y a su vez esta función debe ser llamada antes que SQLAllocStmt(). Sintaxis SQLRETURN SQLConnect (SQLHDBC hdbc, SQLCHAR SQLSMALLINT SQLCHAR SQLSMALLINT SQLCHAR SQLSMALLINT *szDSN, cbDSN, *szUID, cbUID, *szAuthStr, cbAuthStr); Argumentos de la función Tabla 3. Argumentos de SQLConnect. Tipo Argumento del dato uso Descripci&n Entrada SQLHDBC Entrada * SQLCHAR Hdbc SQLSMALLINT SQLCHAR * CbDSN SzUID Handle de conexión Fuente de datos:El nombre o alias de la base de datos. Entrada Longitud argumento del szDSN Entrada ldentificador usuario del SQLSMALLINT CbUlD Entrada Longitud argumento del szUlD SzDSN SQLCHAR SzAuthStr Entrada Cadena autentificacion de (password) SQLSMALLINT CbAuthStr Entrada Longitud argumento del szAuthStr Comentarios Es posible definir varias características (opciones) para función SQLSetConnectOption(). la conexión en la aplicación usando la 20 Losargumentosdelongituden SQLConnect() (cbDSN, cbUID,cbAothSfr) puedenserpuestos como las longitudes actuales delos datos asociados. No se incluyenlos caracteres de terminaci6n de nulo. Se puede usar SQL-NTS en los argumentos de longitud para indicar que el dato asociado esta terminadoen nulo. Los espacios a la izquierda o a la derecha en los valores de szDSN y SZUID son eliminados a menos que estos se encuentren enmarcados en comillas o comillas dobles. Códigos de retorno SQL-SUCCESS SQL-SUCCESS-WITH-INFO SQL-ERROR SQL-INVALID-HANDLE Ejemplo C Listado 2. Conexión a una fuente de datos en C. int connect(SQLHENV henv, SQLHDBC * hdbc) SQLRETURN rc; SQLCHAR server[MAX-DSN-LENGTH t 11, uid [MAX-UID-LENGTH+ 11 , pwd [MAX-PWD-LENGTH+ 1] ; { printf ("Servidor: ; \n") gets ( (char * ) server); printf ("Usuario: ; \n") gets ( (char * ) uid) ; printf ("Password: ; \n") gets ( (char * ) pwd) ; SQLAllocConnect(henv, hdbc);/ * establece un handlede conexión * / rc = SQLConnect (*hdbc, server, SQL-NTS, uid, SQL- NTS, pwd,SQL-NTS); if (rc ! = SQL-SUCCESS) printf("Error al intentar conectara la base de datos\n"); return (SQL-ERROR); 1 else [ printf ("Conexión exitosa\n") ; return (SQL-SUCCESS); 1 Ejemplo VB Listado 3. Conexión a una fuente de datos en VisualBasic. Dim Dim Dim Dim Dim rc as Integer hdbc as Long DSN as String UID as String PWD as String 'Inicializa el handle de conexión rc = SQLAllocConnect(henv, hdbc) DSN = "Nombre Base" 21 UID PWD = = "Tendo" "Kasumi" rc = SQLConnect (hdbc, DSN, Len(DSN), UID, Len(UID), PWD, Len(PWD) ) if rc <> SQL-SUCCESS Then MsgBox "Error al intentar conectar a la basede datos'' Else MsgBox "Conexión exitosa" End If 3.4 Ultimo: Se liberan los recursos usados Hasta ahora hemos visto los pasos necesarios para realizar una conexión a una base de datos, a saber son: obtener el handle de ambiente, obtenerel handle de conexión y realizar la conexiónen sí. Después de esto se pueden hacer todas las consultas que se necesiten a la base de datos y procesar la información obtenida. Pero después de esto que sigue? No es bueno dejar conexiones abiertas a diestra y siniestra, pues afecta al rendimiento del motor de base de datos, toda conexión abierta es un recurso en el servidor que implica memoria y tiempo de procesamiento, a mayor número de procesos, menor tiempo de atención para cada uno. Es por ello que se deben liberar los recursos asociados a una conexión unavez que se obtuvo y procesó la información deseada, cerrandolasconexionesexistentes.Laformadehacerlo en VisualBasicseejemplificaen el fragmento de código que sigue: Dim rc as Integer rc = SQLDisconnect(hdbc) 'Cierra la conexión a la BD rc = SQLFreeConnect(hdbc) 'Libera el handlede conexión y l o s recursos y los recursos rc = SQLFreeEnv (henv) 'Libera el handle de ambiente La función SQLDisconnect, cierra la conexión entreel servidor y la maquina cliente, pero es posible establecer conexión otra vez conla función SQLConnect usando el mismo handle de conexión, sin embargo, la función SQLfreeConnect libera al handle de conexión y ya no es posible usarlo para abrir otra conexión, a menos que sea establecido otra vezcon SQLAllocConnect, lo mismo sucede con el handle de ambiente, con SQLfreeEnv se libera al handle y para poder obtener otro handle utilizable se debe llamar SQLAllocEnv. a La función SQLDisconnect () Propósito SQLDisconnect() cierra la conexión a una base de datos asociada con un handle de conexión. Después de llamar a 6sta función, se puede llamar a SQLConnect() para conectar a otra base de datos o a la misma, o llamar a SQLFreeConnectO para liberarlos recursos del handlede conexión. Sintaxis SQLRETURN SQLDisconnect (SQLHDBC hdbc); n de 22 Argumentos de la función Tabla 4. Argumentos de SQLDisconnect. Tino de dato Hdbc Araumento SQLHDBC uso DeSCriRCidn Entrada conexión Handle de Comentarios Si una aplicación llama a SQLDisconnect antes de que se hayan liberado todos los handles de sentencia asociados con la conexidn, ODBC los libera despubs de que realiza la desconexión de la base de datoscon bxito. Si se regresa SQL-SUCCESS-WITH-INFO, esto implica que aunque la desconexibn de la base de datos fue exitosa, setiene informacibn de implementacibno de un error. Por ejemplo: Se encontro un problema al hacer la limpieza despues de la desconexibn, o, Si no existe una conexidn actual debido a un evento independiente de la aplicacibn (como una falla en la comunicacibn). Despues de un exitoso SQLDisconnect(), la aplicacibn puede reusar hdbc para hacer otro llamado a SQLConnect(). Códigos de retorno 0 SQL-SUCCESS SQL-SUCCESS-WITH-INFO SQL-ERROR 0 SQL-INVALID-HANDLE La función SQLFreeConnect() Propósito SQLFreeConnectO invalida y libera el handle de conexibn. Todos los recursos asociados al ODBC CLI con el handle de conexibn son liberados. Se debe llamar a SQLDisconnect() antes que a esta funcibn. Se puede llamar a SQLFreeEnvO para continuar con la terminacibn del programa, o a SQLAllocHandle(), para obtener un nuevo handle de conexibn. Sintaxis SQLRETURN SQLFreeConnect (SQLHDBC hdbc); Argumentos de la función Tabla 5. Argumentos de SQLFreeConnect. TipoArgumento de dato Handle Entrada Hdbc SQLHDBC Uso Descripción 23 Comentarios Si esta funci6n se llama cuando existe aún una conexi6n abierta, la funci6n regresa SQL-ERROR, y el handle de conexi6n permanece valido. Códigos de retorno SQL-SUCCESS SQL-ERROR 0 SQL-INVALID-HANDLE La función SQLFreeEnv () Propósito SQLFreeEnvO invalida y libera el handle de ambiente. Todoslos recursos del ODBC CLI asociados con el handle de ambiente son liberados. Se debe llamar a SQLFreeConnectO antes que a M a funci6n. Esta funci6n esla última que debe llamar una aplicacidn antes de terminar. Sintaxis SQLRETURN S Q L F r e e E n v (SQLHENV h e n v ) ; Argumentos de la función Tabla 6. Argumentos de SQLFreeEnv. ~~~ Tipo de Argumento dato Uso Descripci6n Handle Entrada henv SQLHENV Comentarios Si ésta funci6n es llamada cuando aún existe un handle de conexi6n vAlido, la funci6n regresa SQL-ERROR, y el handle de ambiente permanece valido. Códigos de retorno SQL-SUCCESS SQL-ERROR 0 SQL-INVALID-HANDLE 3.5 Formato básico de una aplicación ODBC Como se ya se ha visto a lo largo de este capítulo, la creaci6n de una aplicaci6n con la API de ODBC sigue cierto lineamiento en cuanto al orden de llamada de las funciones de la librería de ODBC. Toda aplicaci6n queutilice las funciones de la API debe seguir este formato, desde obtener 24 unhandledeambientehastalaliberacidndelmismo. Es imprescindibletenerencuentaque desconectar una fuente de datos no significa que el handle de conexi6n haya sido liberado, sino que ese mismo handle puede ser utilizado para establecer otra conexi6n con una fuente de datos distinta, mientras no se libere con la funcibn SQLFreeConnect. Se tienepues,unformatodefinido en la partedelaccesoadatosdeunaaplicaci6nque usa ODBC, este consta de la obtenci6n de los handles necesarios, el procesamiento de sentencias de SQL y la liberaci6n de los recursos despues de hacerse las consultas. Como se muestra en la Figura 4, la parte de las consultas puede constarde varios ciclos de procesamiento de sentencias de SQL y de manejo de resultados, este ciclo puede durar tanto tiempo como lo disponga un usuario o según sea la 16gica de la aplicaci6n. Se Obtienen los handles necesarios para la conexidn y se establece la misma Se procesan las consultas mediante un handle de sentencia como se vera en el siguiente capítulo Se desconecta de la fuente y se liberan los handles de conexidn SQLAllocEnv SQLAllocConnect SQLConnect SQLAllocStmt Procesar sentencia SQL Obtener resultados SQLFreeStmt SQLFreeEnv Figura 4. Formato básicode una aplicación ODBC. En el siguiente capítulo se revisa lo concerniente a las consultas. La parte de procesamiento de sentencias de SQL y de manejo de resultados viene ligadoun a handle mas,el cual como se podra suponer, reserva recursos de memoria para las consultas que se envian a la base de datos. Los resultados que se obtienen se procesan según las necesidades de la aplicacidn en particular. Por supuestoque al satisfacer todas las consultas el handledesentenciadebeserliberadoen el código con SQLFreeStmt como buena practica de programacibn, aunque esto es opcional, pues como ya se vio antes, la funci6n SQLDisconnect en el bloque de liberaci6n de recursos los libera en forma autom8tica. 25 3.6 Conexiones múltiples y manejo de errores Una aplicaci6n no necesariamente tiene que estar atada a una sola conexibn, una vez obtenido el handle de ambiente hem, se pueden tener todos los handles de conexi6n hdbc que se deseen y que los recursos de memoria permitan. Un handle de ambiente puede soportar varios handles de conexibn, así como un handle de conexi6n puede soportar varios handles de sentencia hsfmt. El Listado 4 muestra c6mo se realiza la conexion a dos fuentes de datosdistintas utilizando el mismo handle de ambiente, cada conexi6n se hace dentro de una funcidn llamada conect, a la cual se le pasan como argumentos el handle de ambiente por valor, y por referencia una variable de tipo SQLHDBC la cual contendrael valor del handle de conexi6n obtenido para esa fuente de datos. La parte de creaci6n de sentencias para consultas se omite por simplicidad. Listado 4. Ejemplo de conexidn a dos fuentesde datos distintas enC. ........................................................ ** * * Conexión a dos fuentes de datos ** * * Funciones usadas: ** ** SQLAllocConnect SQLDisconnect ** SQLAllocEnv SQLFreeConnect ** SQLConnect SQLFreeEnv ** ......................................................... #include <stdio.h> #include <stdlib.h> / * Declaraciones de l o s tipos de datos y de las funciones ODBC* / #include "sqlcli. h" int connect(SQLHENV henv, SQLHDBC * hdbc); #define #define #define #define MAX-DSN-LENGTH 18 MAX-UID-LENGTH 10 MAX PWD LENGTH 10 MAXICONNECTIONS5 int main() ( SQLHENV henv; / * Declara la variable de ambiente */ SQLHDBC hdbc[MAX -CONNECTIONS]; / * Arreglo de variables de conexión * / / * Obtiene un handle de ambiente * / SQLAllocEnv (&henv); / * Conecta con la primer fuente de datos * / connect (henv, &hdbc [O] ) ; / * Conecta con la segunda fuente de datos * / connect (henv, &hdbc [ 11 ) ; /******** Inicia el procesamiento de resultados * * * * * * * * * / / * Obtener handle de sentencia, ejecutar sentencia, etc. * / / * En esta parte van las consultas y la obtención de */ / * resultados */ /******** Finaliza el procesamiento de resultados * * * * * * * / printf ("\nDesconectando . . . . . \n"); 26 SQLDisconnect (hdbc[Ol) ; / * SQLDisconnect (hdbcrl]) ; /* SQLFreeConnect (hdbc [O]) ; / * SQLFreeConnect (hdbc[l] ) ; /* SQLFreeEnv(henv); /* Desconectando la primer fuente de datos * / Desconectando la segunda fuente de datos * / Liberamos el primer handlede conexión * / Liberamos el segundo handlede conexión * / Liberamos el handle de ambiente * / ..................................................................... ** connect - Pidedatos de conexión y realizalaconexi6n ** ..................................................................... int connect(SQLHENV henv, SQLHDBC* hdbc) SQLRETURN rc; SQLCHARserver SQLCHAR SQLSMALLINT { [MAX-DSN-LENGTH + 11, uid[MAX-UID-LENGTH + 11, pwd[MAX-PWD-LENGTH + 11; buffer[255]; outlen; printf ("Teclear nombre del servidor ->\n"); gets ( (char * ) server); printf ("Teclear nombre del usuario -->\n") ; gets ((char* ) uid); printf("Tec1ear clave del usuario --->\n"); * ) pwd) ; gets( (char SQLAllocConnect(henv, hdbc);/ * obtiene un handle de conexión * / / * SQL-NTS indica que las cadenas pasadas finalizan con nulo * / rc = SQLConnect (*hdbc, server, SQL-NTS, uid, SQL-NTS, pwd, SQL-NTS) ; if (rc ! = SQL SUCCESS) { printf ("ErTor al intentar conectara la base de datos\n") ; return (SQL-ERROR); ) else { printf("La conexiónse realizó con éxito\n"); return (SQL-SUCCESS); 1 1 Observese del código anterior, que dado un handle de ambiente henv, se pueden hacer todas las conexiones que se deseen,el Único requisito es declarar una variable distinta para cada handle de conexi6n hdbc que se requiera; en el caso anterior se utiliz6 un arreglo de tipo SQLHDBC para almacenar los handlesdeconexibnobtenidos, lo cual esunasoluci6nadecuadacuando no conocemos el númerodeconexionesquesevanaestablecer.Porsupuestoquetodaslas conexiones hechas deben cerrarse y tambien los handles de conexidn deben liberarse, por lo que se debe llamar a las funciones SQLDisconnect y SQLFreeConnect por cada conexi6n realizada, la ventaja de declarar un arreglo para los handles de conexibn es que la desconexi6n y liberacidn de recursos se puede hacer dentrode un ciclo For, que resulta muy sencillo de implementar. Manejo de errores Ya se ha visto que todas las funciones de ODBC hasta ahora tratadas, al ejecutarse, regresan un valor que indica si la funci6n se ejecut6 bien o no, este valor es un c6digo de retorno o Return Code, y cada funci6n de ODBC cuenta con un conjunto fijo de los cddigos de retorno que puede 27 regresar. Debido a 6sta caracteristica de las funciones de ODBC en sus códigos de retorno, es posible hacer un manejo sencillo de errores al saber que códigos de retorno podemos esperar, para ello se pueden usar bloques If, Then o bloques Select, Case o Switch según sea el caso. Un lo podemos ver en ellistado de código arriba presentado ejemplo de éste manejo simple de errores del cual extraemos la parte de interés para su estudio enel siguiente fragmento. rc = SQLConnect(*hdbc, server, SQL-NTS, uid, SQL- NTS, pwd, SQL-NTS); if (rc ! = SQL SUCCESS) { printf ("Error al intentar conectar la a base de datos\n") ; return (SQL-ERROR); 1 else { printf("La conexión se realizó con éxito\n"); return (SQL-SUCCESS); 1 El bloque If, Then prueba para el Return Code(rc) SQL-SUCCESS, si el rc no es SQL-SUCCESS, se da por omisión que entonces es SQL-ERROR, pues se ha dado por entendido que no puede ser SQL-INVALID-HANDLE que es el código de retorno restante para la función SQLConnect. Para hacer una prueba exhaustiva delos códigos de retorno usaríamos un bloqueSwitch, como se muestra en el siguiente fragmento de código C: rc = SQLConnect (*hdbc, server, SQL -NTS, uid, SQL-NTS, pwd,SQL-NTS) ; swicth (rc) { case SQL-ERROR: printf("Error al intentar conectar a la base de datos\n"); break; case SQL-SUCCESS: printf("La conexión se realizó con éxito\n"); break; case SQL-INVALID HANDLE: ; printf ("El handle proporcionado es inválido\n") 1 De ésta forma se agotan todas las posibilidades para una función en particular, lo que permite hacerunmanejom&precisode los errores.Conestointroducimoslafuncióndemanejode errores de la API de ODBC, la función SQLError. La función SQLError proporciona información sobre errores y advertencias (warnings),6sta función debe llamarse en cualquier caso en que el y para código de retorno no sea SQL-SUCCESS, especialmente para SQL-ERROR SQL-SUCCESS-WITH-INFO. La función SQLError () Propbito SQLError() proporciona la información de diagnóstico asociada con la llamada mas reciente a una función del CLI para un handle de sentencia, conexión o ambiente en particular. La información consiste de un SQLSTATE normalizado, el código de error nativo, y de un mensaje en texto. Se debe llamar a SQLError() después de recibir un código de retorno de SQL-ERROR o SQL-SUCCESS-WITH-INFO en la llamada a otra función. 28 Sintaxis SQLRETURN SQLError (SQLHENV SQLHDBC SQLHSTMT SQLCHAR SQLINTEGER SQLCHAR SQLSMALLINT SQLSMALLINT henv, hdbc , hs tmt , *szSqlState, *pfNativeError, *szErrorMsg, cbErrorMsgMax, *pcbErrorMsg); Argumentos de la función Tabla 7. Argumentos de SQLError. Tipo de dato Argumento uso Descripción SQLHENV henv Entrada SQLHDBC hdbc Entrada Handle de ambiente. Para obtener la información de diagnóstico asociada a un ambiente, se pasa un handle de ambiente válido. Poner hdbc y hstmt a SQL-NULL-HDBC y SQL-NULL-HSTMT respectivamente. Handle de conexión ala base de datos. Para obtener la información de diagnóstico asociada con una conexión, se pasa un handle de conexión válido,sey pone hstmt aSQL-NULL-HSTMT. El argumento henv es ignorado. Handle de sentencia. Para obtener la información de diagnóstico asociada con una sentencia, se pasa un handle de sentencia válido. Los argumentos henv y hdbc son ignorados. SQLSTATE es una cadena de5 caracteres terminada con el caracter nulo. Los primeros2 caracteres indican la clase del error;los siguientes 3 indican la subclase. Los valores corresponden directamente los a valores de SQLSTATE definidos enla WOpen SQL CAE specification y en la ODBC specification, aumentados con valores de SQLSTATE específicos de IBM y del producto de base de datos. Código de error nativo. Código de error generado por el manejador de base de datosutilizado. Si el error es generado porel CLI y no porel manejador de base de datos (DBMS), éste campoes puesto a-99999. Apuntador al buffero cadena que contendráel mensaje o texto con la descripción del problema. Máxima (esto es, la reservada) longitud del buffer szErrorMsg. La longitud recomendada para reservar es SQL-MAX-MESSAGE-LENGTH + 1. Entrada hstmtSQLHSTMT SQLCHAR * szSqlState Salida SQLINTEGER * pfNativeError Salida SQLCHAR * szErrorMsg Salida SQLSMALLINT cbErrorMsgMax Entrada SQLSMALLINT pcbErrorMsg Salida Apuntador al número total de bytes disponibles enel buffer de salida szErrorMsg (longitud de la cadena de descripción del error). Este no incluye al caracter terminador nulo. Comentarios Los SQLSTATEs son definidos por la WOPEN SQL CAE y la WOpen SQL CLI, aumentados con valores específicos de IBM y del producto de base de datos que se maneje. 29 Para obtener informaci6nde diagn6stico asociada con: El ambiente, pasar un handle de ambiente valido, y poner hdbc y hstmt a SQL-NULL-HDBC y SQL-NULL-HSTMT respectivamente. Una conexion, pasar un handle de conexibn valido, y poner hstmt a SQL-NULL-HSTMT. argumento henv es ignorado. Una sentencia,pasar ignorados. un handledesentenciavalido. El Los argumentos henv y hdbc son Si la informaci6n de diagndstico generada por alguna funci6n del CLI no es obtenida antes de que se llame a cualquier otra funcibn que no sea SQLError() con el mismo handle, la informaci6n para la funci6n previamente llamada se pierde. Esto ocurre aún si nose genera informaci6n para la siguiente funci6n que se llama. Para evitar el truncamiento del mensaje de error, se declara la longitud del buffer de salida de: SQL-MAX-MESSAGE-LENGTH + 1. El mensaje nuncasera mas largo que esto. Códigos de retorno SQL-ERROR SQL-INVALID-HANDLE SQL-NO-DATA-FOUND SQL-SUCCESS Recuperando mensajes de error Si una funci6n que no sea SQLError regresa SQL-ERROR o SQL-SUCCESS-WITH-INFO, una aplicación puede entonces llamar a SQLError para obtener informacibn del error. La aplicaci6n puede necesitar llamar aSQLError mas de una vez para recuperar todoslos mensajes de errorde una función, pues una funci6n puede generar varios mensajes de error. Cuando la aplicaci6n llama a otra funci6n diferente.los mensajes de error para la funcidn previa son eliminados. Se puedetenerinformacidnadicionaldeerrores o deestadoprovenientedealguna de las siguientes fuentes: De una funci6n de ODBC, indicando que se detect6 un error de programacibn. O deunafuente de datos,indicandoqueocurri6 un errordurante el procesamiento deuna sentencia. La información dada por SQLError esta en el mismo formato que la de la especificacibn para el SQLSTATEen el X/Open and SQLAccessGroupSQLCAEspecification (1992). Notar quela funci6n SQLError nunca regresa información de error para sí misma. Mensajes de error de ODBC ODBC define una arquitectura por capas para conectar una aplicacibn con una fuente de datos. En su forma mas simple, una conexi6n ODBC requiere de dos componentes: el Driver Manager y un driver. Una conexi6n mas compleja podría incluir mas componentes: el Driver Manager, un cierto número de drivers, y un cierto número (posiblemente diferente) de DBMS’s. La conexi6n podría ser a traves de varias plataformas de computaci6n y de sistemas operativos y usar una variedad de protocolos de red. 30 Conforme la complejidad de una conexión de ODBC se incrementa, es mas importante proporcionarmensajesdeerrorconsistentes y completospara la aplicacibn, los usuarios, y el personaldesoporte.Losmensajesdeerrorno sblo debenexplicar el error,tambikndeben proporcionarlaidentidad del componenteenelcualocurrib.Laidentidad del componentees particularmente importante para el personal de soporte cuando una aplicacibn utiliza componentes de ODBC dedistintas compaiíías. Puesto queSQLError no regresa la identidad del componente en el cual ocurrió el error, esta informaci6n debe estar incrustada en el texto del mensaje de error. Formato del mensaje de error Los mensajes deerror proporcionados por SQLError vienen de dos direcciones: fuentes de datos y componentes en una conexibn ODBC. Típicamente,las fuentes de datos no soportan directamente ODBC. Consecuentemente, si un componente en una conexibn de ODBC recibe un mensaje de error de una fuente de datos, este debe identificar la fuente de datos como la fuente del error. Debe ademas de identificarse a sí mismo comoel componente que recibi6 el error. Si la fuente del error es el componente en si, el mensaje de error debe describirlo así. Por eso, el mensaje de error dado por SQLError tiene dos formatos diferentes: uno para errores que ocurren en una fuente de datos y otro para errores que ocurren en otros componentes de una conexibn de ODBC. Para errores que no ocurren en la fuente de datos,el texto tiene el formato: [identificacibn de/ proveedo~[identificacibn del componente deODBC] texto suministradopor el componente Para errores que ocurren en una fuente de datos, el texto tiene el formato: [identificacibn del proveedor][identificaci6n de/ componente de ODBC] [identificacibn de /afuente de datos] texto suministradopor la fuente de datos La siguientetabla muestra el significado de cada elemento. Tabla 8. Componentes del mensaje de error de ODBC. Elemento Significado identificación del proveedor Identifica vendedor al o proveedor del componente que en el ocurrió el erroro que recibió el error directamente de la fuente de datos. identificacióndelcomponentede ODBC Identificaelcomponente en el queocurrióelerror o que recibió el error directamentede la fuente de datos. identificaciónde la fuentededatos Identificalafuentededatos.Paradriversdeunnivel,éstees típicamente un identificador detipo o formato de archivo, tal como Xbase’. Para drivers multinivel, éste es el nombre del DBMS. Generadopor el componentede ODBC. Generadoporlafuentededatos. textosuministrado por elcomponente textosuministrado por la fuente de datos 1. En éste caso,el driver actúa comotal y como fuente de datos. Note que los brackets (0) se incluyen enel texto del error; no indican elementos opcionales. 31 3.7 Los códigos de retorno y los tipos de datos Cuando una aplicaci6n llama a una funci6n de ODBC, el driver ejecuta la funci6n y regresa un &digo de retorno predefinido. Estos c6digos de retorno indican estados de dxito, advertencia o falla en la funci6n. La Tabla 9 define los c6digos de retorno para las funciones de ODBC. Tabla 9. Códigos de retorno delas funciones deODBC. ~ retorno Código de Valor Descripción SQL-SUCCESS O La función se completó satisfactoriamente, información adicionaldel SQLSTATE. SQL-SUCCESS-WITH-INFO 1 La función completó se satisfactoriamente, con una advertencia u otra información.Usar SQLErrorO para obtener el SQLSTATE e informacióndel error. SQL-NO-DATA-FOUND 1O0 La función se ejecutó bien, pero no se encontró información en el conjunto de resultados, pues se han leido todas las lineas y se llegó al final del mismo. SQL-ERROR -1 SQL-INVALID-HANDLE -2 función La falló debido handle una inválido (ambiente, conexión o de sentencia) pasado comoun argumento de entrada. SQL-STILL-EXECUTING 2 Una función que fué iniciada forma enasíncrona continua ejecutándose. SQL-NEED-DATA 99 Mientras procesaba se una sentencia, el driver determinó que la aplicación necesita enviar datos de parámetros. función SQLErrorO Usar falló. Laobtener para e información del error. no existe el SQLSTATE La aplicaci6n es responsable de tomar las acciones apropiadas basadas en el cbdigo de retorno que se genera. Los tipos de datos Las siguientesdeclaracionesde definicidnde tipomuestran los tiposdedatosusadosenlos ejemplos de c6digo enC mostrados en dsta obra.Su equivalencia con los tipos de Visual Basicse indican en el comentario que sigue ala declaracidn. char f t ypede int typede long f short int t ypedef typedef double t ypedef float t ypedef void * typedef PTR typedef long SQLCHAR; SQLINTEGER; SQLSMALLINT; SQLDOUBLE; SQLREAL; PTR; SQLPOINTER; HENV; / * equivale a Stringen VB * / / * equivale a Long en VB */ / * equivale a Integeren VB * / / * equivale a Doubleen VB * / / * equivale a Singleen VB * / / * sin equivalencia en VB * / / * sin equivalencia en VB * / / * equivale a Long en VB * / t y p e d e fl o n g t y p e d e fl o n g t y p e d e fl o n g t y p e d e f HENV t y p e d e f HDBC t y p e d e f HSTMT t y p e d e f HDESC t y p e d e f SQLINTEGER t y p e d e f RETCODE HDBC ; HSTMT; HDESC; SQLHENV; SQLHDBC; SQLHSTMT; SQLHDESC; RETCODE; SQLRETURN; / * e q u i v a l e a Long en VB * / / * e q u i v a l e a Long en VB * / /* e q u i v a l e a Longen /* /* /* /* equivale equivale equivale equivale equivale VB */ a Long e n VB a Long en VB a Longen VB a I n t e g e r en a I n t e g e re n */ */ */ / * e q u i v a l e a Long e n VB * / /* VB VB */ */ Estas definiciones tambikn se aplican en los tipos de datos referidos dentro de las sintaxis de las funciones de ODBC que se exponen en esta parte. Se puede observar que la mayoría de ellos equivalen a tipos de datos simples en Visual Basic, excepto porel tipo de dato apuntador el cual no tieneequivalenciaenVisualBasic. MAS adelantesever&unaformaindirecta de manejode apuntadores en Visual Basic cuando se analice la funci6n SQLBindCol. 4 El manejo de instrucciones de SQL En el capítulo anterior se estudiaron en detalle las funciones dela API para hacer la conexión con una fuente de datos, cómo es que se deben de llamar Bstas, es decir, el orden de llamada; sus características mas sobresalientes, como sonlos códigos de retorno que cada una puede regresar, e incluso se discutió sobre el manejo de errores. En este capítulo ahondaremos en la parte que quedópendiente: la recuperacióndeinformacidn y el manejodelamisma.Tambiensetratará sobre el acceso a la base de datos tanto para la lectura, como parala escritura o actualización de los datos en ella. 4.1 Lo primordial: El handle de sentencia Antes de que podamos realizar cualquier instrucción de lectura o escritura hacia la base de datos necesitamos reservar recursos para tales operaciones. AI igual que como ocurrió parael ambiente y la conexión, se necesita un handle mas, y este sera para la sentencia (recuerdese que laAPI de ODBC esta desarrollada enC, el cual es el lenguaje de los apuntadores). La formade obtener &te último handleen Visual Basic es como sigue, con la función SQLAllocStmt: Dim rc As Integer, hstmt As Long rc = SQLAllocStmt(hdbc, hstmt) SilafunciónregresaSQL-SUCCESS,setieneunhandledesentenciaasociadoalabasede datos dada por el handle de conexión hdbc, el handle de sentencia obtenido puede usarse para hacer todas las operaciones de SQL que se deseen, al terminar de usarlo se debe liberar con la función SQLFreeStmt por limpieza, aunque se libera autometicamente con SQLDisconnect. Debe tenerseencuentaqueesposibletenermasde un handle de sentenciaasociadosaunode conexidn. 33 34 La función SQLAllocStmt () Propósito SQLAllocStmt() obtiene un nuevo handle de sentenciay lo asocia con la conexibn especificada por el handle de conexibn que se pasa como argumento. No existe un número definido de handles de sentencia que pueden ser creados para una misma conexibn. Se debe llamar a SQLConnectO antes queMa a funcibn. EstafunciónsedebedellamarantesquealasfuncionesSQLBindParamO,SQLPrepareO, SQLExecuteO, SQLExecDirectO, o que cualquier otra funcibn que tenga un handle de sentencia como uno de sus argumentos de entrada. Sintaxis SQLRETURN SQLAllocStmt (SQLHDBC hdbc, SQLHSTMT *phstmt); Argumentos dela función Tabla I O . Argumentos de SQLAllocStmt. ~~ Tipo de dato Argumento Entrada SQLHDBC SQLHSTMT Salida* hdbc phstmt Uso Descripción Handle de conexión Apuntador al handle de sentencia Comentarios ElODBCCLIusacadahandledesentenciapararelacionartodos los descriptores,resultados, información del cursor e informacibn de estado para la instruccibn SQL procesada. Aunque cada instruccibndeSQLdebetenerunhandledesentencia,sepuedenreusar los handlespara diferentes instrucciones. Para ejecutar una actualizacibn o borrado por posicibn, la aplicacibn debe usar diferentes handles de sentencia parala instruccibn SELECT y para las instrucciones UPDATEo DELETE. Si el apuntador al handle de sentencia (phstmt) apunta a un handle de sentencia valido obtenido por una llamada previa a SQLAllocStmt(),el valor original del mismo es sobrescrito como resultado de la llamada a esta funcibn. Códigos de retorno SQL-SUCCESS 0 SQL-ERROR 0 SQL-INVALID-HANDLE Si se regresa SQL-ERROR, el argumento phsfmf es puesto a SQL-NULL-HSTMT. La aplicacibn debe de llamar a SQLErrorO con el mismo hdbc y con el argumento hstmt puesto a SQL-NULL-HSTMT. 35 4.2 SQLExecDirect: Ejecutar instrucciones de SQL Despues de obtener el handle de sentencia, ya podemos mandar nuestros comandosy consultas al DBMS de nuestrabase de datos. Los tipos de instrucciones de SQL que podemos usar son muy variados,algunas de lascualespuedenregresar o no conjuntos de resultados,setienenpor ejemplo: instrucciones de consulta: SELECT. Regresa un conjunto de resultados. instruccionesdemodificacióndedatos:INSERT,UPDATE,DELETE.Noregresanun conjunto de resultados. instrucciones de modificaciónde la basededatos:CREATETABLE, regresan un conjunto de resultados. DROPTABLE. No instrucciones de comandos: EXECUTE. Si ejecuta un Stored Procedure que regresa valores entonces tambiBn regresa un conjunto de resultados. y otras específicas del DBMS que se este utilizando. pero toda instrucci6n se construye y procesa en forma de cadena de caracteres o String. Para hacer esto utilizamos la funciónSQLExecDirect de la API de ODBC, que en un programa de Visual Basic quedaríacomo sigue: Dim rc As Integer, query As String query = "SELECT * FROM TABLA" rc = SQLExecDirect (hstmt, query, Len (query) ) para procesar una instrucción, Bsta se pasa como un argumento de cadena, tambiBn se necesita pasar la longitud en bytes dela cadena; si el código fuera en C, bastaría utilizar parala longitud de la cadena la macro SQL-NTS, para indicar que la cadena de caracteres es una cadena terminada con el carhcter terminador nulo. Unavez hecha la consulta se utilizael mismo handle de sentencia paraobtener los resultadosde Bsta, si esquelainstruccibnpasadacomoargumentoregresa resultados. La función SQLExecDirect () Propósito SQLExecDirect ejecuta en forma directa la instrucción de SQL especificada. Las sentencias sólo pueden ser ejecutadas una a la vez. AdemBs, el servidor de la base de datos conectada debeser capaz de prepararla sentencia. Sintaxis SQLRETURN SQLExecDirect (SQLHSTMT SQLCHAR SQLINTEGER hs tmt , *szSqlStr, cbSqlStr); 36 Argumentos de la funci6n Tabla 11. Argumentos de SQLExecDirect. Tiipo de dato Argumento Uso SQLHSTMT hstmt Entrada Handle sentencia. de Descripción No debe existir ningun cursor abierto asociado con hstmt, verSQLFreeStmt para más información. SQLCHAR szSqlStr Entrada Cadena de la sentencia de SQL. El servidor de base de datos conectado debe ser capaz de preparar la sentencia. SQLINTEGER cbSqlStr Longitud del contenido del argumento szSqlStr. La longitud debe ser ya seala longitud exacta de la cadena de sentencia, o si la cadena de sentencia está terminada en nulo, utilizar SQL-NTS. Entrada Comentarios La sentencia SQL no puede ser COMMIT o ROLLBACK, paraello se usa la funcidn SQLTransactO. ParamásinformacidnsobrelassentenciasSQLsoportadasreferirsea la documentacidndel controlador de ODBC utilizado. LacadenadelainstruccidnSQLpuedecontenermarcadoresdeparámetros.Unmarcadorde "?", eindicaunaposicidnenlasentenciadondese parámetroserepresentaporuncaracter sustituirá el valordeunavariabledelaaplicacibn,cuandoSQLExecDirectOesllamado. SQLBindParamO liga (o asocia) una variable de la aplicacidn a cada marcador de parametro, para indicar si cualquierconversidndedatossedeberealizar al tiempoenque los datosson transferidos. Todos los parametros deben estar ligados antes de llamar a SQLExecDirectO. Si la sentencia es un SELECT, SQLExecDirectO genera un nombre de cursor, y enseguida abre el cursor. Si la aplicacidn ha usado SQLSetCursorName() para asociar un cursor con el handle de sentencia, El CLI asocia el nombre de cursor generado por la aplicacidn con el cursor generado internamente. Pararecuperaruna línea dedatos del conjuntoderesultadosgeneradoporunasentencia SELECT, se utiliza SQLFetchO despues de que SQLExecDirectO se ejecutd con 6xito. Si la sentencia SQL es un DELETE por posicibno un UPDATE por posicidn,el cursor referenciado en la sentencia debe estar posicionado en unalínea de datos. Adicionalmente la sentencia de SQL debe definirse en un handle de sentencia separado bajo el mismo handle de conexidn. No debe haber un cursor abierto asociado con el handle de sentencia. Códigos de retorno SQL-SUCCESS SQL-SUCCESS-WITH-INFO SQL-ERROR SQL-INVALID-HANDLE SQL-NO-DATA-FOUND SeregresaSQL-NO-DATA-FOUND si lasentenciaSQLesunUPDATEdebúsqueda DELETE de búsqueda yno hay líneas que satisfagan la condicidn de búsqueda. o un 37 4.3 SQLBindCol: Asociando variablesy columnas Lafunción SQLBindCol permite ligar variables con las columnas de un conjunto de resultados, facilita la conversidn de tipos de datos y permite una programacidn rnhs sencilla que la que se tendría utilizando únicamente la funcibn SQLGefDafa, pararecuperarcolumnasindividuales.La funcidn asocia una direccibn de memoria con una columna de datos y las subsecuentes llamadas a SQLfetch colocan los datosdelacolumna y renglbndentrodelalocalidaddememoria especificada. Sin embargo, su utilizacidnen Visual Basic no es tan trivial, ya que por ser este un lenguaje de alto nivel, presenta algunas deficiencias en cuanto a las facilidades que otorga al programador para el manejo de memoria frente a otros lenguajes de nivel medio, como C por ejemplo; aun mas, en Visual Basic no existeel concepto de apuntador-tan común en C - q u e es esencial para el usode algunas funciones de ODBC que devuelven o requieren direcciones de memoria. El problema de los apuntadores puede sortearse usando algunas de las funciones de la Windows API; con 6stas se pueden reservar espacios de la memoriay obtener los apuntadores a estos dentro de variables de tipo Long como se ver$ mas adelante en este apartado. Despuésdeenviarunaconsultade SQL,unaaplicacidndeODBCliga los resultadosdeesa consulta con variables. Comúnmente se asocia una variable con cada columna del conjunto de resultados. La siguiente es la declaracibn de SQLBindCol que debe usarse en un programa Visual Basic: Declare Function SQLBindCol Lib "Odbc32 .dll" ( ByVal hstmt As Long, ByVal icol As Integer, ByValfCTypeAsInteger, rgbValue As -Y, ByVal cbValueMaxAs Long, pcbValue As Long As Integer Esencialmente se pasa un apuntador a SQLBindCol en el argumento rgbValue. Despues el driver el apuntadorencada deODBC coloca los datos en la localidad de memoria especificada por ejecucidn de SQLfetch. Posteriormente la aplicacidn puede recuperar los datos referenciando a esa localidad de memoria. El manejo de variables de Visual Basicy SQLBindCol A diferencia de los otros tipos de datos, las variables de tipo String son movidas constantemente en la memoria por Visual Basic, esto con el fin de queel contenido de 6stas no est6 particionadoy se encuentre completo dentro de una misma phgina de memoria, así como para eficientar el uso deesta.Debidoaellono es recomendableusarvariablesdetipo String directamenteconla funcidn SQLBindCol, puesaunquesepaseladireccidndelavariabledecadenamediantela palabra reservada ByVal, como se muestra a continuacibn: Dim sCadena AsString, rc As Integer, lCadena As Long sCadena = SpaceS(255,O) 'Reserva espacio en la cadena rc = SQLBindCol(hstmt, 1, SQL-C-CHAR, ByVal sCadena, 254, 1Cadena) no existe garantia de que la cadena permanecer$en la misma direccidn de memoria antes de que se llame a la funcibn SQLFetch. Usar cadenas de VB con SQLBindCol puede resultar no s610 en datos incorrectos sino también en Fallos de Proteccidn General. 38 Sin embargo, si es posible utilizar con seguridad los tipos numericos como Long o Integer, pues &tos ocupan localidades fijas de memoria y Visual Basic las respeta. El cbdigo siguiente correra sin problemas: Dim Numero As Long, rc As Integer rc = SQLBindCol(hstmt, 1 , SQL-C-LONG, Numero, O , O ) Nbtesequenoesnecesarioespecificar el tamanodelbufferconcbValueMax ni recuperarla longituddeldatoenpcbValue,comoseríapara un datodetipo SQL-C-CHAR. Tampocofue necesario usar la palabra reservada ByVal, pues a diferencia de las cadenas, utilizarla implicaría pasar el contenido de la variablenumkrica y no su direccibn en memoria,lo cual sería incorrecto. Es posible pasar la direccibn de una cadena de VB a SQLBindCol pero como las cadenas son movidas en la memoria por VB, noes seguro usar SQLBindCol sin primero bloquearla memoria. A continuacibn se discute el uso de cadenas de VB con SQLBindCol y las llamadas a las funciones de la API de Windows GlobalAlloc, GlobalLock, lstrcpyn, GlobalUnlock y GlobalFree para reservar, bloquear y copiar datos dela memoria a cadenas de Visual Basic. Usando cadenas deVisual Basic con SQLBindCol En Visual Basic, para pasar un apuntador de cadena a una DLL, se utiliza la palabra reservada ByVal precediendo ala variable de cadena. Cuando se utiliza la palabra ByVal se le dice tambikn a Visual Basic que pasara una cadena terminadaen nulo, o sea, una cadena cuyo último caracter es un "carácter nulo" (cero binario). Es raramente posible (y muy arriesgado) usar cadenasde VB con SQLBindCol, siempre y cuando no se manipulen otras cadenas (incluyendoel texto de los controles) duranteel lapso de tiempoen que las cadenas est& ligadas. Durante el procesamiento normal, como ya vimos, VB reorganiza peribdicamente su espacio de cadenas. Esto implica que las cadenas se moveran en memoria y sus direccionesnoseranlasmismas.Comoresultadodeesto,lasdireccionespasadasa SQLBindCol yanotendranvalidez.Pero,siaúndelodichoanteriormente,setieneundeseo desmedido de usar cadenas deVB con SQLBindCol se pueden seguir las siguientes reglas: No manipularotrascadenasentrelasllamadasaSQLBindCol y SQLFreeStmtcon SQL-UNBIND. Un ejemplo común es que durante un ciclo de SQLFetch se concatenen las variables ligadasen una sola cadenay despues anadir 6sta a una caja de texto. Cuando se liguen cadenas, ligar cadenas delongitud variable, y no cadenas de longitud fija. Las cadenas de longitud variable se declaran así: Dim mysfr as String No ligar arreglos de cadenas. Los arreglos de cadenas se almacenan en segmentos64K de y Visual Basic podría mover los elementos del arreglo para recobrar espacio de cadena fragmentado.Comoresultado de esto, el elementodelarreglo al queunacolumnaeste ligado podria volverseinvididoenunsiguienteSQLFetch.ComoSQLExtendedFetch requiere específicamente ligas con arreglos, se recomienda no usar SQLExtendedFetch en Visual Basic. Antesdeligarlascadenasdelongitudvariable,sedebeninicializarcomosigue,para reservar espacio de memoria para la cadena. mysfr = Sfring$(255, O) 39 Cuando se llame a SQLBindCol y se ligue a una cadena (si rgbValue sera una cadena), usar la palabraByval. Por ejemplo, SQLBindCol(hsfmt&, 7, SQL-C-CHAR, Byval mystr, cbMax&, cbVal&) Usando memoria global: funciones de la WinAPl Es posibleusar las funciones de la APIdeWindows:GlobalAlloc,GlobalLockylstrcpynpara obtener una porcidn dememoria fija, ligar las columnas con SQLBindCol a 6sta memoria, y copiar los datos desdela memoria hasta cadenas de Visual Basic. GlobalAlloc: Reserva un bloque de memoria y regresaun handle a la memoria si la funcibntiene 6xito. Se debe reservaruna porcidn de memoria por cada columna que se desee ligar. GlobalLock: Bloquea la memoria reservada con GlobalAlloc y regresa un apuntador ala memoria. Se debe usarGlobalLock para obtener un apuntador el cual usar con SQLBindCol. Istrcpyn: Copia el contenidode la memoriadesde una direcci6naotraun número específico de bytes. Se debe usarlstrcpyn para copiarel contenido de la memoria a la cadena de longitud variable. Nota: Cuando se termine de usar la memoria se deben llamar a las funciones Globalfree para liberarla memoria yel selector usado porGlobalAlloc. GlobalUnlock y Se deben añadir las siguientes declaraciones (Listado 5) en la seccibn (general) (declarations) de algún m6dulo de cddigode la aplicaci6n Visual Basic: Listado 5. Declaraciones necesariaspara usar las funcionesde memoria de la Win A P L 'Constantes para GlobalAlloc Public Const GMEM-FIXED= &HO Public Const GMEM-INVALID-HANDLE= &H8000 Public Const GMEM-MOVEABLE= &H2 'Declaraciones de las funcionesde memoria Declare Function lstrcpynLib "kerne132" Alias "IstrcpynA"( ByVal lpStringl AsAny, ByVal lpString2 As Any, ByVal iMaxLength As -Long ) As Long Declare Function GlobalAlloc Lib "kerne132" ( ByVal wFlags As Long, ByVal dwBytes As Long ) As Long Declare Function GlobalLock Lib "kerne132" (ByVal hMem As Long) As Long Declare Function GlobalUnlock Lib "kerne132" (ByVal hMem As Long) As Long Declare Function GlobalFree Lib "kerne132" (ByVal hMem As Long) As Long El Listado 6 ilustra el uso de las funciones de la WinAPl junto con la funcibn SQLBindCol para obtener datos de tipoSQL-C-CHAR, el manejo de errores se omite por simplicidad: 40 Listado 6. Uso de la función SQLBindCol con variables de cadena. Private Sub Fom-Click0 -"1 "" 'Para la conexión 1"""""""""""""""""""""""- Dim henv As Long 'Handle de ambiente Dim hdbc As Long 'Handle de conexión I"""""""""""""""""""""""- 'Para la sentencia 1"""""""""""""""""""""""- Dim hstmt As Long 'Handle de sentencia Dim rc As Integer 'Código de retorno Dim sql As String 'Sentencia SQL 1"""""""""""""""""""""""- 'Para SQLBindCol 1"""""""""""""""""""""""- 'Cadena para contener el dato de la columna Dim sCadena As String 'Longitud deldato regresado en la columna Dim 1CadenaLenAs Long 'Apuntador a la memoria para ligar la columna Dim 1pAddrCadena As Long 'Handle de memoria Dim hMemCadena As Long 'Longitud máxima del buffer de almacenamiento Dim cbValueMax As Long 'Obtenemos el handlede ambiente rc = SQLAllocEnv (henv) 'Obtenemos el handle de conexión rc = SQLAllocConnect(henv, hdbc) 'Efectuamos la conexión rc = SQLConnect (hdbc, "BaseDatos", 9, "Usr", 3, "Pass", 4 ) 'Obtenemos un handle de instrucción rc = SQLAllocStmt(hdbc, hstmt) 'Ejecutamos directamente la instrucción sql = "SELECT Columna FROM Tabla" rc = SQLExecDirect (hstmt, sql, Len(sq1) 1"""""""""""""""""""""""- 'Preparamos todo para SQLBindCol 1"""""""""""""""""""""""- cbValueMax = 254 I 'Inicializamos el espacio en la cadena I sCadena = String$ (255, O) 1 'Reservamos memoriay obtenemos un apuntador a ésta 1 hMemCadena = GlobalAlloc(GMEM-MOVEABLE, Len(sCadena)) 1pAddrCadena = GlobalLock(hMemCadena) Debug.Print 1pAddrCadena 'Ligamos la columnacon el apuntador. Porser de tipo Long, la 'variable 1pAddrCadena se pasa por valor para quesu pase contenido, 'el cual es el apuntador al espacio de memoria reservado. 41 rc = SQLBindCol(hstmt, 1, SQL-C-CHAR, ByVal IpAddrCadena, cbValueMax,1CadenaLen) I 'Hacemos el Fetch 1 rc = SQLFetch(hstmt) 1 'Copiamos la cadena de la memoria a una cadena de VB,se toma 'en cuenta el caracter terminador nulo por lo que se añade 1 1 Call lstrcpyn(ByVa1 sCadena, ByVal IpAddrCadena, 1CadenaLen + 1) Debug.Print sCadena 1 'Desligamos columnasy liberamos memoria I rc rc rc = = = SQLFreeStmt (hstmt, SQL-UNBIND) GlobalUnlock(hMemCadena) GlobalFree(hMemCadena) 1 'Desconectamos y liberamos handles I rc rc rc rc = = = = SQLFreeStmt(hstmt, SQL-DROP) SQLDisconnect (hdbc) SQLFreeConnect(hdbc) SQLFreeEnv (henv) End End Sub El cbdigo anterior es un ejemplo muy sencillo para ligar solamente una columna conuna variable. Para ligar más columnas se debe reservar memoria y obtener un apuntador a la misma con las funciones GlobalAlloc y GlobalLock por cada columna que se desee ligar, como se hizo en el ejemplo. Se pudo haber usado la funcidn lstrcpy enlugarde lstrcpyn, la funcibn lstrcpy puede copiar cadenas terminadas en carácter nulo a variables de cadena de VB sin necesidad de indicar la cantidad de los bytes a transferir. Pero siendo estrictos, la funcidn lstrcpyn es la más adecuada para el caso. La función SQLBindCol () Propósito SQLBindCol()asocia(liga)columnasdeunconjuntoderesultadosavariablesdeaplicacidn (búffers de almacenamiento), para todoslos tipos de datos. La informacidn es transferida desdeel DBMS hasta la aplicacibn cuando la funcibn SQLFetch() es llamada. Esta funcibn también se usa para especificar cualquier conversibn de datos que se requiera. Se le llama una vez por cada columnadel conjunto de resultados quela aplicacidn necesite recuperar. Las funciones SQLPrepareO o SQLExecDirectO se llaman usualmente antes que a esta funcibn. También puede ser necesario llamar a las funciones SQLDescribeCol() o SQLColAttributes(). SQLBindCol() debe ser llamada antes que a SQLFetchO, que transfiere los datos a los búffers de almacenamiento especificados con ésta funcibn. 42 Sintaxis hs tmt, icol, fCType, rgbValue , cbValueMax, *pcbValue); SQLRETURNSQLBindCol(SQLHSTMT SQLSMALLINT SQLSMALLINT SQLPOINTER SQLINTEGER SQLINTEGER Argumentos de la función Tabla 12. Argumentos de SQLBindCol. __ Tipo dato deArgumento hstmtSQLHSTMT SQLSMALLINT icol SQLSMALLINT fCType uso Descripción Entrada Handle de sentencia Entrada Número identificandola columna que se desea ligar. Las columnas se numeran secuencialmente, de izquierda a derecha, iniciando en 1. Entrada Tipo de dato enla aplicación parala columna con número icol enel conjunto de resultados. Se soportan los siguientes tipos: o O O O o O O o Salida (diferida) SQLPOINTER rgbValue SQL-CHAR SQL-VARCHAR SQL-NUMERIC SQL-DECIMAL SQL-INTEGER SQL-SMALLINT SQL-FLOAT SQL-REAL SQL-DOUBLE SQL-GRAPHIC SQLVARGRAPHIC SQL-DATETIME SQLTYPE-DATE SQL-TYPE-TIME SQLTYPEJIMESTAMP Especificando SQL-DEFAULT causa queel dato sea transferido en su tipo de dato normal. Apuntador al buffer dondeel CLI almacenaráel dato cuando se llame aSQLFefcb. Si rgbValue es nulo, la columna está desligada. Entrada SQLINTEGER cbValueMax SQLINTEGER * pcbValue Salida (diferida) Tamaño del buffer rgbValue en bytes disponible para almacenar los datos de la columna. Si fCType es ya sea SQL-CHAR o SQL-DEFAULT, entonces cbValueMax debe ser> O o de lo contrario la función regresa un error. Apuntador al valor que indicael número de bytes de información almacenados enel buffer rgbvalue. SQLFetch() regresaSQL-NULL-DATA en éste argumento si el dato de la columna es nulo. 43 Nota: Para 6sta funcidn, tanto rgbValue como cbValue son salidas diferidas, lo que significa que las localidades de almacenamiento a las que esos apuntadores apuntan no son actualizadas hasta queSQLFetch()esllamada.Laslocalidadesreferenciadasporesosapuntadorespermanecen validas hastaque se llama aSQLFetchO. Comentarios Una aplicación llama a SQLBindCol()una vez por cada columna del conjunto de resultados que se quiera recuperar. Cuando se llama a SQLFetchO, la informaci6n en cada una de esas columnas ligadas es colocado enel lugar asignado (dado por los apuntadores rgbValue y cbValue). La aplicacidnpuedeconsultarprimero los atributosdelacolumna(talescomotipodedato y longitud) llamando a SQLDescribeCol() o a SQLColAttributes(). Esta informaci6n puede entonces ser usada para especificar el tipo de dato correcto de lasAreas de almacenamiento, o para indicar conversiones de datos aotros tipos. En extraccionesde datos posteriores, la aplicación puede cambiarlas asociaciones alas columnas o ligar otras columnas llamando a SQLBindCol(). Las nuevas ligas no aplican a datosya extraídos, sino que son usadas en la siguiente llamada a SQLFetch(). Para desligar una columna, se llama a SQLBindCol()con el valorde rgbValue puestoaNULL.Paradesligartodaslascolumnas, la foption puestoa aplicaciónpuedellamaraSQLFreeStmtOcon el argumentodeentrada SQL-UNBIND. No es posible desligar columnas individuales con SQLFreeStmt. Las columnas se identifican con un número, asignado secuencialmente de izquierda a derecha, comenzando con 1. El número decolumnasen el conjuntoderesultadospuededeterminarse llamando a las funciones SQLNumResultCols() o SQLColAttributes(), esta última conel argumento fdescType puesto a SQL-DESC-COUNT. Una aplicación puede escoger no ligar todas las columnas, o aún no ligar ninguna columna. Los datos en las columnas no ligadas (y solamente de las columnas no ligadas) pueden recuperarse usandoSQLGetDataOdespuesdellamaraSQLFetch().SQLBindCol()esmaseficienteque SQLGetDataO, y debe ser usado siempre que sea posible. La aplicación debe asegurarse de que se ha reservado el suficiente espacio de almacenamiento para la informaci6n a ser recuperada. Si el buffer es para contener datos de longitud variable, la aplicacióndebereservartantoespaciodealmacenamientocomolamaximalongitudque la columna ligada requiera, de lo contrario el dato puede truncarse. Si ocurre el truncamiento de un dato de cadena, se regresa SQL-SUCCESS-WITH-INFO y el valor de pcbValue es puesto a la longitud actual restantede rgbValue para enviar hacia la aplicacibn. Códigos de retorno SQL-SUCCESS SQL-ERROR SQL-INVALID-HANDLE 44 4.4 SQLFetch: Navegar por los resultados Despues de que se ha ejecutado una sentencia SQL de consultao un SELECT, podemos usar el mismo handle de sentencia pasado a la funcidn SQLExecDirect para navegar por el conjunto de resultados y posicionarnos sobre la línea deseada para despues obtener los datos con la función SQLGetData o desde las variables ligadas con SQLBindCol. Para cumplir con esta misión tenemos a la funcidn SQLFetch. La funcidn SQLFefch mueve el cursor asociado con un handlede sentencia una línea hacia delanteen el conjunto de resultados,la forma en que se haría esto en Visual Basic es la siguiente: Dim rc As Integer rc = SQLFetch(hstmt) SQLFetch regresa SQL-SUCCESS cuando se ha posicionadocon &xitoen la siguiente línea de los datos. Cuando el cursor esta en el último rengldn de datos y se llama a SQLFetch, esta regresa SQL-NO-DATA-FOUND, indicando quese ha llegado al final del conjunto de resultados. Para recorrer todoel conjunto de informacidn se debe utilizar SQLFetch dentro un deciclo iterativo, por ejemplo un ciclo while sería muy conveniente, ya que prueba primero la condicidn antes de lo queesadecuadocuandoesposiblequela ejecutarlasinstruccionesdesubloqueinterno, consulta arroje un conjunto vacío de resultados. La forma en que se haría esto es como sigue: Dim rc As Integer SQLFetch(hstmt) While KC <> SQL-NO-DATA-FOUND 'Obtener l o s datos con SQLGetData 'Poner el cursor en la siguiente línea rc = SQLFetch(hstmt) Wend KC = Obskrvese que en el caso de que la consulta asociada conhstmf sea un conjunto vacío, es decir, que la consulta no arroje resultados, el valor del cddigo de retorno de SQLFetch sera en forma inmediata SQL-NO-DATA-FOUND, así pues al evaluar la condicidn delciclo while esta sera falsa, por lo quela parte interna del mismo sera pasada por alto. La función SQLFetch () Propósito SQLFetchO posiciona el cursor en la siguiente linea del conjunto de resultados, y recupera las columnasqueesténinvolucradasen la consulta.SepuedeusarSQLFetchOpararecibirdatos directamente en las variables que sean especificadas con SQLBindCol(), o se pueden recuperar lascolumnas enformaindividual despub del fetch,llamandoa SQLGetDataO.Lafuncidn SQLFetchO tambien realiza la conversidn de datos,si se indicd esta cuando la columna fue ligada con SQLBindCol. Sintaxis SQLRETURN SQLFetch (SQLHSTMT hstmt) ; 45 Argumentos de la función Tabla 13. Argumentos de SQLFetch. Tipo de dato Argumento Uso Descripción SQLHSTMT sentenciade Handle Entrada hstrnt Comentarios SQLFetchO s610 puedeserllamadosilamasrecientesentenciaejecutada SELECT. El número de variables de aplicacibn ligadas con SQLBindCol() no debe exceder columnas enel conjunto de resultados o SQLFetchO fallara. en hstmt, fue un al número de Si SQLBindCol() no ha sido llamado para ligar columnas, entonces SQLFetchO no regresa datos a la aplicación, y únicamente avanza el cursor. En 6ste caso se puede llamar a SQLGetDataO para obtenertodas las columnasenformaindividual.Losdatosdelascolumnassondescartados cuando SQLFetchO avanzael cursor a la siguiente línea. Si algunavariableligadaacolumnano esdelacapacidadadecuadaparacontener el dato regresadopor SQLFetchO, el datoestruncado.Si los datosquesontruncados son datosde carácter, la funcibn regresa SQL-SUCCESS-WITH-INFO, y se genera un SQLSTATE que indica el truncamiento. El argumento de salida de SQLBindCol() pcbValue, contiene la longitud del dato recuperado de la columna. La aplicacibn puede comparar la longitud de la salida con la longitud de la entrada (argumentos @Value y cbValueMax en SQLBindCol()) para determinar que columnas de caracteres han sido truncadas. El truncamiento de los tipos num6ricos no está senalado si el truncamiento implica dígitos a la derecha del punto decimal. Si el truncamiento ocurre ala izquierda del punto decimal, se devuelve un error y el SQLSTATE de truncamiento de dato. El truncamiento de tipos de datos grdficos se trata igual que con los tipos de datos de caracter. Excepto que el búffer rgbValue se llene al múltiplo m& cercano a dos bytes y que permanezca menor o igual queel valor de cbValueMax especificado en SQLBindCol(). Cuando todas las líneas han sido extraídas del conjunto de resultados, o las líneas restantes no sean necesarias, se debe llamar a SQLFreeStmtO para cerrarel cursor y desechar la informacibn remanente de la consulta los y recursos asociados a la misma. Códigos de retorno SOL-SUCCESS SQL-SUCCESS-WITH-INFO SQL-ERROR SQL-INVALID-HANDLE SQL-NO-DATA-FOUND La funcibn devuelve SQL-NO-DATA-FOUND si no existen renglones de datos en el conjunto de resultados, o las llamadas anteriores a SQLFetchO han abarcado todas las lineas del conjunto de resultados. 46 4.5 SQLGetData: Recuperando la información por columnas Cuando el cursor ya se encuentra ubicado enla línea de datos que nos interesa podemos iniciar la extracción de los datos de cada una de las columnas. Tocael turno a la funci6n SQLGetData, bsta funci6n extrae la informaci6n de la columna que se le indique y la convierte al tipo de dato que tambibn se le pasa como argumento. Se debe llamar a la funci6n SQLGefData por cada columna que se desee recuperar, para lo cual es muy útil la funci6n SQLNumResultCols, la cual determina elnúmerodecolumnasconlasquecuentaelconjuntoderesultados. El usodebstasdos funciones se ejemplificaen el Listado 7, el manejo de errores se omite por sencillez. Listado 7. Uso de las funciones SQLGetData y SQLNumResultCols. Const 1ongBuffer As Long = 255 Dim rc As Integer Dim IAs Integer Dim cadFija As String * 1ongBuffer Dim longDato As Long Dim NumCols As Long Dim Datos( ) As String 'Ejecutar la consulta rc = SQLExecDirect(hstmt, "Select * From Tabla", 19) 'Obtiene elnúmero de columnas rc = SQLNumResultCols (hstmt, NumCols) 'Ir al primer renglónde datos rc = SQLFetch(hstmt) 'Obtiene l o s datos de todas las columnas ReDim Datos(1 To NumCols) As String For I = 1 To NumCols cadFija = String$ (longBuffer, O) longDato = O rc = SQLGetData(hstmt, I, SQL-C-CHAR, cadFija, longBuffer, longDato) If longDato < 1 Then Datos (I) = '"' 'Columna con dato nulo Else Datos (I) = Left$ (cadFija, longDato) End If Next I En el ejemplo anterior cabe resaltar el uso de una cadena fija como argumento de salida de la funci6n SQLGefData. La raz6n de esto es que la funci6n requiere de un buffer de longitud fija para colocar la informacibn, la longitud maxima del buffer esta dada por el argumento longSufer. El otro en argumento de salida,longDato, indica el número de bytes de informaci6n valida cadfija. Como se vera en los siguientes parrafos, el argumento de salida cadFija es un apuntador al buffer contenedor del dato en la versi6n C de la función; tambibn el argumento representado porlongDato es un apuntador a una variable de tipo long int, mas sin embargo, el truco consiste en declarar el argumento como ByVal rgbValue As String en lugar de void * rgbValue como seria en la versi6n en C de la funci6n. Lo anterior funciona con SQLGetData, pero no con SQLBindCol, la versi6n Visual Basic de la funci6n SQLBindCol declara rgbValue como Any. La raz6n de esto estriba enel manejo de Visual Basic delas variables de tipo cadenao String: Visual Basic reacomoda automaticamente las variables de tipo cadena en la memoria, de forma que el contenido de las variables permanezca 47 integro dentro de una misma paginade memoria, es por ello queaún si obtenemos el apuntador a una variable de tipo cadena con LSTRCPY, al hacer VB el reordenamiento de las variables, la variablepara la queobtuvimos el apuntadortendra unanuevadireccibndememoria. AI usar SQLGetData, la funcibnpasainmediatamente el datoen la columnaa la variabledecadena representada por rgbvalue, y aunque esta sea reacomodada en la memoria por VB, la informacibn queyacontienenoesalterada.Encambio,para el caso de SQLBindCol, aunquepasemosla direccibn de la variable de cadena con ByVal cadfija, éSta puede cambiar antes de efectuarse el Fecth y la direccibn antes pasadaya no sera valida. Los resultadosson impredecibles. La función SQLGetData () Propósito SQLGetDataO recuperadatosdealgunadelascolumnasen la Ihea actual del conjunto de resultados. Esta funcibn es una alternativa a SQLBindCol(), lacual transfiere datos directamente a variables de la aplicacidn en las llamadas aSQLFetchO. SQLGetDataO puede usarse también para leer datosde carActer de gran tamano por fragmentos de éstos. Se debe llamar a SQLFetch() antes que a SQLGetDataO. Despues de llamar a SQLGetDataO para cada columna, siguiente renglbnde datos. Se llama a SQLFetchO para obtener el Existe otra funcibn llamada SQLGetCol() identica a SQLGetDatao, ambas funciones se soportan por compatibilidad. Sintaxis hstmt , icol, fCType, rgbvalue, cbValueMax, *pcbValue); SQLRETURNSQLGetData(SQLHSTMT SQLSMALLINT SQLSMALLINT SQLPOINTER SQLINTEGER SQLINTEGER Argumentos de la función Tabla 14. Argumentos de SQLGetData. Uso Argumento dato de Tipo Descripcidn sentencia de Handle Entrada SQLHSTMT hstmt SQLSMALLINT icol Entrada Número columna la de cual la de desea serecoger el dato. La primer columna se indica con el numero 1. SQLSMALLINT fCType Entrada Tipo columna dato de la de identificada por icol. Se soportan los siguientes tipos: O O O SQL-CHAR SQLVARCHAR SQL-NUMERIC SQL-DECIMAL SQL-INTEGER SQL-SMALLINT SQL-FLOAT 48 Tipo de dato Argumento Uso Descripci6n O O O O SQL-REAL SQL-DOUBLE SQL-GRAPHIC SQL-VARGRAPHIC SQL-DATETIME SQLTYPE-DATE SQLTYPE-TIME SQLTYPE-TIMESTAMP SQLPOINTER rgbValue Salida Apuntador búffer donde al será almacenado dato el recuperado dela columna. SQLINTEGER cbValueMax Entrada Tamaño máximo búffer delapuntado rgbValue por SQLINTEGER * pcbValue Salida Apuntador al valor que indica número el bytes de disponibles en el bufferrgbvalue. Si el dato está siendo recuperado en piezas, contieneel número de bytes faltantespor recuperar, sin incluirlos bytes de las columnas que han sido obtenidos en llamadas previas a SQLGetDataO. El valor esSQL-NULL-DATA si el valor del dato es nulo. Si éste apuntador está como NULL y SQLFetchO ha obtenido una columna que contiene un nulo, ésta función fallará puesto que no tiene forma de reportar ésto. Si SQLFetchO ha extraido una columna que contiene datos gráficos, el apuntador a pcbValueno debe ser nulo o ésta función fallará porque no tendrá medios para informarle a la aplicación sobre la longitud del dato recuperado en el buffer rgbvalue. Comentarios SQLGetDataO puede ser usadajunto con SQLBindCol() para la misma línea, siempre queel valor de icol no especifique una columna que haya sido ligada. Los pasos generales son: 1. SQLFetchO-avanza el cursor a la primer linea, extrae el primer renglón, transfiere los datos de las columnas ligadas. 2. SQLGetDataO-transfiere los datos de columnas específicas (no ligadas). 3. Repetir el paso 2 para cada columna que sea necesaria. 4. SQLFetchO-avanza el cursor a la siguiente línea, extrae el siguiente renglón, transfiere los datos de las columnas ligadas. 5. Repetir los pasos 2, 3 y 4 paracadarenglón del conjunto de resultados, o hasta que' el conjunto de resultadosya no sea necesario. SQLGetDataO recupera columnas voluminosas si el tipo de dato C (fCType) es SQL-CHAR o si fCType es SQL-DEFAULT y el tipo de la columna esCHAR o VARCHAR. EncadallamadaaSQLGetDataO, si lalongituddeldatoaregresar es mayor o iguala cbValueMax,ocurreuntruncamiento. El truncamiento se indica con el código de retorno de la función de SQLSUCCESS-WITH-INFO aunado a un SQLSTATE que denota truncamiento de datos. La aplicacidn puede entonces llamar a SQLGetDataO nuevamente, con el mismo valor de icd, paraobtener el resto dela informacióndelamismacolumnainiciandoen el puntode 49 truncamiento.Paraobtenerlacolumnaentera, la aplicacidnrepitelasllamadashastaquela funcidndevuelve el cddigo de retorno SQL-SUCCESS.La siguientellamadaa SQLGetDataO regresara el cddigo de retornoSQL-NO-DATA-FOUND. Paradescartarlaspartes de datosrestantesporrecuperar, la aplicacidnpuedellamara SQLGetDataO con el valor de icol puestoalasiguienteposicidndecolumna de interés.Para descartar los datos no recuperados de una linea entera, la aplicaci6n debe llamar a SQLFetchO para avanzar el cursor a la siguiente linea; o, si ya no hay interés en ningún dato mas del conjunto de resultados, se llama a SQLFreeStmtO para cerrar el cursor. El argumento de entrada fCType determina el tipo de conversidn de datos (si existe) necesaria antes de que el datoseacolocadoen el areadealmacenamientoapuntadapor rgbValue. El contenido regresado en rgbValue siempre esta terminado en nulo a menos que se haya usado SQLSetEnvAttrOparacambiar el atributo SQL-ATTR-OUTPUT-NTS. Si la aplicacidnestá recuperando los datosenvariaspartes,éstadebehacer los ajustesadecuados(porejemplo, eliminar el byte de terminacidnen nulo antes de concatenar todas las piezas de nuevo). El truncamiento de tipos de datos numericos no se reporta si el truncamiento implica dígitos a la derecha del punto decimal. Siel truncamiento ocurre ala izquierda del punto decimal,se genera un error. Códigos de retorno SQL-SUCCESS SQL-SUCCESS-WITH-INFO SQL-ERROR SQL-INVALID-HANDLE SQL-NO-DATA-FOUND Se regresa SQL-NO-DATA-FOUND la información parauna columna. cuando la llamada anterior aSQLGetDataO ha obtenido toda Se regresa SQL-SUCCESS si una cadena de longitud cero es recuperada por SQLGetDataO. Si éste es el caso, pcbValue contiene O, y rgbValue contiene un terminador nulo. Si lallamadaa indefinido. SQLFetchO falla, SQLGetDataOno debellamarse yaque el resultadosera 4.6 Esquema general de procesamiento de instrucciones de SQL En la Figura 4 del capítulo 3 de ésta obra, se observa el formato basic0 de una aplicacidn OBDC, en ella se aprecia que entre la conexi6n a una fuente de datos y la liberación de los recursos asociados existe una parte intermedia que consiste en el procesamiento de las instrucciones de SQL. En esta parte vamos a expandir ése bloque para mostrarlo con mas detalle. El siguiente esquema ilustra la forma en que se hacen las llamadas a las funciones de ODBC dependiendodeltipo de instrucciónde SQLquesetrate.Lasfuncionesrelacionadasconun 50 SELECT,difierendelasqueserelacionanconun INSERT,UPDATE o DELETE.LaFigura 5 muestralasdistintasfuncionesde ODBC relacionadasconinstruccionesdeSQL y el ordende preferenciadellamadaquetendríanenunaaplicaci6n,dependiendode los distintostiposde sentencias de SQL que hay. Reservar handle de sentencia SQLAllocStmt() + -t + I Ejecutar sentencia directa SQLSetParamO SQLExecDirectO Preparar sentencia SQLPrepareO SQLSetParamO . + + I I I I Ejecutar sentencia SQLExecuteO v Actualización de datos (UPDATE, DELETE, INSERT) SQLRowCount() Recuperar datos de una consulta (SELECT) SQLNumResultColsO SQLDescribeCol() CREATE, DROP, GRANT, REVOKE (no requieren función) O SQLColAttributes() SQLBindCol() SQLFetchO SQLGetDataO 1 Liberar handle de sentencia SQLFreeStmtO SQLTransactO ~~ 1 ~ Figura 5. Esquema de procesamiento de sentencias. 5 Resumen de funciones de ODBC Todas las funciones de la API de ODBC empiezan con SQL y estBn distribuidas dentro de tres nivelesinclusivos:Núcleo,Level 1 (Nivel 1) y Level 2 (Nivel 2), estaformadeclasificacibnes conveniente, pero ligeramente artificial desde el punto de vista de programacibn. En 6ste capítulo y se vera su distribucibn se harh un recuento de las funciones que conforman la API de ODBC dentro de los tres niveles de conformidad establecido porSAG el CLi. 5.1 Niveles de conformidad El grupo nuclear de funciones (Core API) corresponde a las funciones de la especificacibn de la WOpenandSQLAccessGroupCallLevelInterface.Losotrosdosgrupos(Niveles 1 y 2) son extensiones de la SAG CLI specification. La siguiente lista resume la funcionalidad incluida en cada nivel de conformidad: Núcleo API Reservar y liberar handles de ambiente, conexibny sentencia. Conexibn a fuentes de datos.Uso de sentencias múltiples en una conexibn. Preparar y ejecutar sentencias SQL. Ejecucibn inmediata de sentencias SQL. Asignaci6ndeBreasdealmacenamientoparaparbmetrosenunasentenciaSQL columnas de resultados. y para Recuperacibn de datos de un conjunto de resultados. Obtener informacibn sobreel conjunto de resultados. Commit o roll back de transacciones. Recuperar informacibn sobre errores. 51 52 (Nivel 1) Level 1 API Funcionalidad del núcleo API. Conexibn a fuentes de datos con cajas de dialogo especificasdel driver. Puesta y consulta de los valores de las opcionesde sentencia y conexibn. Envío de parte o de todo el valor de un parametro (útil para datos extensos). Recuperacibndeparte extensos). o detodo el valor de unacolumnaderesultado (útil paradatos Recuperacibndeinformacibndecatalogo(columnas,columnasespeciales,estadísticas, tablas). y Recuperacibn de informacibnsobrecapacidadesdeldriver y de la fuentededatos,tales como tiposde datos soportados, funciones escalares, y funciones ODBC. (Nivel 2) Level 2 API Funcionalidad del núcleo API y de Level 1 API. Búsqueda de informaci6n de conexi6n y listado de las fuentes de datos disponibles. Envío de arreglos de parametros. Recuperacibn de arreglos de resultados. Recuperacibn del número de parametros y descripcibn de los parametros individuales. Uso de cursoresnavegables. Recuperacibn de las formas nativas de las sentencias SQL. Recuperacibn de informacibn de catalogo (privilegios, llaves y procedimientos). 5.2 Funciones y niveles de conformidad A continuacibn se hace un recuento de las funcionesde ODBC por nivel de conformidad. Tambien se da una breve descripci6n para cada una de ellas. Funciones del núcleo SQLAllocConnect Obtiene un handle de conexibn. SQLAllocEnv Obtiene unhandle de ambiente. Un handle de ambiente seusa para unao mas conexiones. SQLAllocStrnt Reserva un handle de sentencia. SQLBindCol Asigna area de almacenamiento para una columna y especifica el tipo del dato. SQLCancel Cancela una sentenciaSQL. 53 SQLColAttributes Describe los atributos de una columna del conjunto de resultados. SQLConnect Conecta a un driver específico por nombre de fuente de datos, usuario, y contrasena. SQLDescribeCol Describe una columna en el conjunto de resultados. SQLDisconnect Cierra la conexibn. SQLError Regresa informacibn adicional sobre errores o de status. SQLExecDirect Ejecuta una sentencia. SQLExecute Ejecuta una sentencia preparada. SQLFetch Regresa una línea de resultados. SQLFreeConnect Libera el handle de conexibn. SQLFreeEnv Libera el handle de ambiente. SQLFreeStmt Finaliza el procesamiento de sentenciasy cierra el cursor asociado, descarta resultados pendientesy opcionalmente libera todoslos recursos asociados con el handle de sentencia. SQLGetCursorName Regresa el nombre del cursor asociado con el handle de sentencia. SQLNumResultCols Regresa el número de columnas en el conjunto de resultados. SQLPrepare Prepara una sentencia SQL para su posterior ejecucibn. SQLRowCount Regresa el número de líneas afectadas por una instrucci6n insert, update o delete. SQLSetCursorName Especifica un nombre de cursor. SQLSetParam Asigna almacenamiento para un pardmetro en una sentencia SQL. SQLTransact Confirma o deshace (Commit o Roll Back) una transaccibn. 54 Funciones del Nivel 1 SQLColumns Regresa unalista de nombres de columnas de las tablas especificadas. SQLDriverConnect Conecta aun driver específico mediante una cadena de conexidn o permite que el Driver Managery el Driver desplieguenun dialogo de conexi6n para el usuario. SQLGetConnectOption Regresa el valor de una opci6nde conexi6n. SQLGetData Regresa parteo toda una columna de una línea de un conjunto de resultados. (Util para datos extensos.) SQLGetFunctions Regresa las funciones soportadas por el driver SQLGetlnfo Regresa informacibn sobre un driver específico y de la fuente de datos. SQLGetStmtOption Regresa el valor de una opci6nde sentencia. SQLGetTypelnfo Regresa informaci6n sobrelos tipos de datos soportados. SQLParamData Regresa el valor de almacenamiento asignado un a parametro cuyos datos serdn enviados en tiempo de ejecuci6n. (Util para datos extensos.) SQLPutData Envía parte o todo el dato de un pardmetro. (Util para datos extensos.) SQLSetConnectOption Establece opciones de conexibn. SQLSetStmtOption Establece opciones de sentencia. SQLSpecialColumns Recupera informaci6n sobreel conjunto de columnas que únicamente identifican un a registro en una tabla especificada, y las columnas que son actualizadas automdticamente cuando cualquier valordel registro es actualizado por una transacci6n. SQLStatistics Recupera las estadísticas de una tabla y la lista de indices asociados con la tabla. 55 Funciones del Nivel 2 SQLBrowseConnect Regresa niveles sucesivosde atributos de conexi6ny valores de atributos válidos. Cuando se ha especificadoun valor para cada atributo de conexibn, realiza la conexi6n a la fuente de datos. SQLColumnPrivileges Regresa una lista de columnas ylos privilegios asociados a &stas, para una o mas tablas. SQLDataSources Regresa una lista de las fuentes de datos disponibles. SQLDescribeParam Regresa la descripci6n de un parametro específico en una sentencia. SQLExtendedFetch Regresa múltiples líneas de resultados. SQLForeignKeys Regresa unalista de columnas que son llaves externas (foreign keys), si tales columnas existen enla tabla especificada. SQLMoreResults Determina si existen m& registros de resultados disponibles y, si los hay, inicializa el procesamiento para el siguiente conjunto de resultados. SQLNativeSql Regresa el texto de una sentenciaSQL como fue traducida por el driver. SQLNumParams Regresa el número de parametros enuna sentencia. SQLParamOptions Especifica el uso de valores múltiples para parámetros. SQLPrimaryKeys Regresa la listade nombres de columnas que son la llave primaria de una tabla. SQLProcedureColumns Regresa lalista de parametrosde entrada y salida, así como las columnas que conforman el conjunto de resultados arrojado por los procedimientos almacenados especificados. SQLProcedures Regresa la lista de nombres de procedimiento almacenados en una fuente de datos. SQLSetPos Posiciona un cursor dentro de un bloque de registros extraídos la de fuente de datos. SQLSetScrollOptions Establece las opciones que controlan el comportamiento del cursor SQLTablePrivileges Regresa una lista de las tablasy los privilegios asociados con cada tabla. 56 Para más información Existe en Internet gran cantidad de sitios relacionados con ODBC. A continuaci6n se mencionan los m& recomendables, los cuales fueron visitados porel autor de dste trabajo: En esta direcci6n se encuentra una biblioteca electrbnica en linea sustentada por IBM. En ella existen varios libros electrónicos con temas computacionales incluyendo ODBC. Cuenta con un buscador de libros por tema: La dirección siguiente es la deun manual en línea proporcionado por la compafiía Solid Information Technology Ltd. Esta compafiía distribuye un servidor de base de datos llamado SOLID Server que El manual en línea es la guía y referencia del programador tambikn cuenta con un driver de ODBC. de SOLID Server y presenta varios conceptos importantes de ODBC, asi como la descripcibn de las funciones de laAPI de ODBC: httD://www.solidtech.com/support./Da/HTTOC.HTM Es muyuti1 tambien la dirección del soporte en linea de Microsoft. En ella sepuedeencontrar y ODBC: informaci6n relacionada con Visual Basic httD://suDport.microsoft.com La siguientetambikn es una biblioteca en línea de IBM: PARTE II. La biblioteca de funciones O Sobre la biblioteca de funciones O Conexion.bas: La biblioteca de funciones O Uso de las funciones b Sobre la biblioteca de funciones En este capítulo se describiran algunos de los aspectos de la construcción de la biblioteca de funciones. Se hablara un poco sobre los pormenores en las que estan cimentadas. Se discutird el Ambit0 de acción delas funciones de la biblioteca,y que es lo que pueden y no pueden hacer. 6.1 Objetivo y alcance de las funciones Las funciones que se analizaran en bsta segunda parte sonel resultado de un proyecto que surgió a raíz de un convenio firmado entre PEMEXREFlNACIdN y la UAM Iztapalapa. El convenio incluía el desarrollo de sistemas para cubrir problemdticas propias del corporativo PEMEX. Uno de los primeros sistemas que se decidió desarrollar implicaba exhaustivos accesos a la base de datos, cuyoBack-enderaunServidordelnformix 7.0, por lo que el driver utilizado para los accesos desde el Front-end fue un driver denominado INFORMIX-CLI. Este driver cumple con los niveles de conformidad del Núcleo, Nivel 1 y parte del Nivel 2 delaAPIdeODBC.Laherramientade desarrollofueVisualBasic 4.0, podríapensarsequedadalaherramienta los mecanismosde acceso a los datos hubieran sido lógicamente los propios de Visual Basic y Microsoft, como son RDO o DAO, e incluso tablas vinculadas de Access,lo cual habría facilitado la programación, pero seencontr6quedados los requerimientosdevelocidaddeaccesoyrespuesta,asícomola magnitud del flujo de información, se necesitaba algo mas directo y de mejor rendimiento, por lo que se optó finalmente por usar la API de ODBC. Un servidor, y autor de bsta obra, trabajócasi un mes enel desarrollo de unasuite de funciones en Visual Basic basadas en la API de ODBC, que fueran de facil uso y comprensión para el resto de los miembros del equipo de desarrollo. A diferencia de las propias funciones de la API de ODBC, que pueden resultar oscuras y difíciles de usar para alguien que no haya tenido experiencia conel lenguajeC, las aquípresentadassonbastantesencillas y hasta cierto gradointuitivas.Estas funciones fueron creadas para minimizarel tiempo de aprendizaje delas mismas por el resto de los 59 60 miembros del equipo, el cual habría sido considerable si se hubiera tratado de las funciones de la API, y también para eficientar el acceso los a datos. Cabe resaltar que estas funciones NO proporcionan todas las Características dela API de ODBC, sólo las mas importantes, como sonla conexión a la fuente de datos,la ejecucidn de sentencias y la recuperacidn de los datos. Otras funcionalidades propias de la API no fueron implantadas por razones de tiempo y de limitaciones del lenguaje (por ejemplo la inexistencia de apuntadores), entre las funcionalidades no implantadas se tienen el uso de sentencias preparadas, los cursores navegables, el ligado de columnas con variables, sdlo por mencionar algunas. Sin embargo, las funciones que se lograron construir cubrieron con éxito las exigencias del proyecto y funcionaron muy bien. Con esto no quiero decir que las funciones sean infalibles, es mas, alguien que vea el código podra encontrar varios fragmentos que son susceptibles de mejorar, lo que quiz2 lleva al verdadero objetivo de las funciones: Que sean mejoradas y que implementen mayor funcionalidad de la API de ODBC, o en su defecto, que estimulen el uso de la API de ODBC. Una de las metas del autor es llevar M a s funciones a su formamas general: una librería de enlace dinamico o DLL, para que puedan ser usadas mas alla de Visual Basic, lo que las convertiría en una verdadera librería de funciones parala conexión a basesde datos. 6.2 Tipos de Datos Abstractosutilizados Las definiciones de tipos que se presentan a continuación fueron ideadascon el fin de encapsular en estructuras sencillas los handles usados por las funciones de ODBC y presentarlos deuna formamasamigable,ademas,todaslasestructurascuentan con camposadicionalesque dan información importante sobre otras características. También se redefinen los códigos de retorno para presentarlosen una versión en espaAoly que sean mas faciles de recordar. El tipo Base de Datos El tipo BaseDatos encapsula a los handles de ambiente y de conexidn para ser usados de forma interna por las funciones dela biblioteca de funciones. Una variable de tipo base de datos alojaen su interior los handles de ambiente y de conexión obtenidos, el número de tablas que existen los nombresdelasmismas.Desafortunadamenteenla dentro de labase dedatos,asícomo construcción de la función ConectarBD no hubo tiempo de agregarlas dos ÚRimas características, por lo que 6sta función sólo sirve para portar los handles de ambiente y conexidn y que sean pasados juntos hacia otras funciones que los necesiten. Public Type BaseDatos ENV As Long ID As Long NumTablas As Integer NomTablasO As String End Type 'Handle de ambiente 'Handle de conexión 'Número de t a b l a s en la BD 'Nombres de las tablas dela BD El tipo Registro de SQL El tipo RegistroSQL es una estructura auxiliaral tipo ConsultaSQL que se ver2mas adelante. En 61 se guardan dentro de un arreglode tamaAo variable (DatosCof) los datos recogidos como cadenas arrojados por las consultas ejecutadas con la función HacerConsultaSQL. Tiene tambikn un campo 61 llamado NurnRegAcf, el cual indica el número de rengldn o fila sobre la que esta posicionado el cursor en el conjunto de resultados. El número de las columnas del conjunto de resultados se registra en un campo de la estructura ConsulfaSQL. Private Type RegistroSQL NumRegAct As Integer 'Número de registro actual Datoscol() As String 'Arreglo de cadenas con los campos del registro End Type El tipo Consulta de SQL Este tipo es el mas utilizado de todos. Las variables de tipo ConsulfaSQL guardan dentro de su estructura al handle de sentencia usado por las funciones HacerConsulfaSQL, CerrarInsfrucSQL, SigRegConsulfaSQL y otras. Guardan tambi6n otros datos relevantes a la consulta, 6stos son: la cadena de la instruccidn SQL, el número de columnas del conjunto de resultados, el número de registros o renglones del conjunto de resultados, el tipo SQL de cada columna, si las columnas aceptan o no nulos, los nombresde las columnas regresadas-tal y como estan en la tabla o en la instrucción SQL-y finalmente una estructura de tipo RegisfroSQL, la cual contiene los datos del registro sobreel que est6 posicionadoel cursor. Public Type ConsultaSQL ID As Long 'Handle de sentencia InstSQL As String 'Texto de la instrucción SQL NumCols As Integer 'Número de columnas obtenidas NumRegs As Long 'Número de registros obtenidos SQLTipoColO As Integer 'Número del tipo SQL de la columna NomTipoCol ( ) As String 'Nombre del tipo SQL de la columna AceptaNuloCol ( ) As Long 'Si la columna acepta nulos o no NomCol ( ) As String 'Nombres de las columnas Registro As RegistroSQL 'Datos del registro actual End Type Definiciones de constantes para códigos deretorno Las siguientesdefinicionesdeconstantessonunatranscripci6n al espaliol delasconstantes usadas por las funciones de la API de ODBC para los cddigos de retorno; bstas constantes utilizan el mismo valor num6rico que las otras, por lo que es indistinto el uso de dstaso las de la API. 'Valores de retorno de las funciones = -2 Global Const HANDLE-INVALID0 As Long Global ConstERROR-FUNC As Long = -1 Global ConstEXITO-FUNC As Long = O Global ConstEXITO-FUNC-CON-INFO As Long = 1 Global Const SIGUE-EJECUTANDO As Long = 2 Global Const NECESITA-DATOS As Long = 99 Global ConstNO-HAY-MAS-DATOS As Long = 100 62 6.3 Llamadas a la API de ODBC involucradas Las funciones de la biblioteca estAn construidas sobre llamadas a la API de ODBC. Las llamadas utilizadas son en su mayor partelas del núcleo de funciones y en menor grado las del nivell.S610 se us6 una funci6n del nivel2. En la Tabla 15 se relacionan las funciones de la API de ODBC y las funciones de la biblioteca en las que fueron aplicadas. Tabla 15. Relación de funciones deODBC usadas en la biblioteca de funciones. Llamadade ODBC Niveldeconformidad SQLAllocEnv Núcleo ConectarBD ConectarBD2 SQLAllocStmt Núcleo EjecutarSQL HacerConsultaSQL HacerEscrituraSQL HacerEscDirectaSQL HacerConsultaSQL *SQLColAttributesString Núcleo HacerConsultaSQL Núcleo SQLColAttributes Núcleo cleo Núcleo Funcionesen que se aplica ConectarBD ConectarBDZ NúcleoSQLAllocConnect Núcleo API SQLConnect ConectarBD SQLDisconnect DesconectarBD ConectarBD ConectarBD2 SQLError DescErrorODBC SQLExecDirect EjecutarSQL HacerConsultaSQL HacerEscrituraSQL HacerEscDirectaSQL RepUltimaConsulta SQLFetch Núcleo AntRegConsultaSQL HacerConsultaSQL PriRegConsultaSQL SigRegConsultaSQL UltRegConsultaSQL SQLFreeConnect Núcleo DesconectarBD ConectarBD ConectarBD2 SQLFreeEnv Núcleo DesconectarBD ConectarBD ConectarBD2 Núcleo 63 Llamada de ODBC Nivel de conformidad API Funciones en que se aplica CerrarlnstrucSQL EjecutarSQL HacerConsultaSQL HacerEscrituraSQL HacerEscDirecta RepUltimaConsulta SQLFreeStmt SQLNumResultCols Núcleo HacerConsultaSQL SQLRowCount Núcleo HacerEscrituraSQL HacerEscDirectaSQL SQLDriverConnect Nivel 1 ConectarBD2 SQLGetData Nivel 1 PriRegConsultaSQL SigRegConsultaSQL UltRegConsultaSQL AntRegConsultaSQL SQLSetConnectOption Nivel 1 ConectarBD ConectarBD2 SQLSetStmtOption Nivel 1 HacerConsultaSQL RepUltimaConsulta SQLNumParams Nivel 2 HacerEscrituraSQL HacerEscDirectaSQL * La función SQLColAtfributesStringes un ALIAS para la funciónSQLColAWbutes, la diferencia que tiene la rgbDesc como ByVal String en lugar de declararlo como primera de la segunda es que declara el argumento Any. esto se hizo para poder recuperar atributos de texto en cadenas de caracteres. Algunas funciones de la biblioteca, como HacerConsulfaSQL, utilizan varias de las funciones de la API, encambiootras, sdlo incluyenuna o dosdeellas,como esel casodelafunción DescErrorODBC, la cual sdlo usa la funcidn SQLError. Entre m& funciones de la API de ODBC estén incluidas enel cddigo de una funcidn de la biblioteca, mas compleja y elaborada es ésta. Sin embargo, existe una funcidn que noutiliza absolutamente ninguna de las llamadas ala API. Me refiero a la funcidn ErroresNafivos, la cual obtiene los cddigos de error nativos del driver de ODBC que se esté manejando.esta funcidn y las demas seran estudiadas en el siguiente capítulo. 7 Conexion.bas:Labiblioteca de funciones En 6ste capítulo se presentaran las funciones que componenla biblioteca de funciones, accesible paraVisualBasiccon el M6dulollamado Conexiombas. El m6duloConexioncuentaconlas definiciones de tipos de datos y los cuerpos de las funciones queya se han estado mencionando. En los siguientesapartadosseestudiarán6stasfunciones junto con sus característicasmás relevantes. 7.1 La función ConectarBD Esta es la primer funci6n que una aplicaci6n VB debe usar. Con ella se establece la conexi6n a la fuente de datos dada porel argumento DSN. Se requiere declarar antes una variable como de tipo BaseDatos para que se utilice como argumento de salida de la funcibn. La variable servirá para identificar a la fuente de datos a la que se ha hecho conexi6n y que pueda ser usada por otras funciones. Sintaxis CODRET ConectarBD ( DSN ByVal UID ByVal PSW ByVal BD ByValMostrarErr, CadErrores As String, As String, As String, As BaseDatos, As String - ) 65 66 Argumentos de la función Tabla 16. Argumentos de ConectarBD. Tipo de Argumento dato String DSN Entrada Nombre fuente la datos de Uso Descripción tal y como encuentra se en el administrador de fuentes de datos de ODBC. String UID EntradaNombredeusuarioválidoreconocido datos. String PSW Entrada Contraseña para BaseDatos BD SalidaldentificadorparalaBasedeDatosa conexión. porla fuentede UD. la quesehace Boolean MostrarErr Entrada Bandera para indicar a la función si desea se o no que ésta despliegue una ventana con el mensaje de error dado por el driver. String CadErrores Salida Texto completo error del arrojado por función laprivada DescErrorODBC,tal y como lo reporta el driver de ODBC. Comentarios La función ConectarBD es la primer funcidn de la biblioteca de funciones que se debe utilizar para realizar un acceso a base de datos. Despues de 6sta funcibn, generalmente se usan las funciones HacerConsultaSQL o EjecutarSQL.AIfinalizar las consultassedebecerrarlaconexidnconla función DesconectarBD. Códigos de retorno EXlTO-FUNC EXITO-FUNC-CON-INFO ERROR-FUNC 7.2 La función ConectarBDZ Esta función tiene el mismo propósito que la función ConectarBD,la única diferencia con M a es la forma en la que se realiza la conexión. Con la función ConecfarBD2 en lugar de especificar por separado el nombre de la fuente de datos, el nombre de la cuenta de usuario y la contraseña, estos datospueden incluirse dentrodeunacadenadeconexión, la cualserapasadacomo argumento de entrada a la función. Si la función determina que los datos para la conexión no son suficientes desplegara una ventana propia del driver de ODBC utilizado que le pedira al usuario la informacióndeconexiónfaltante. Si el usuario elige CANCELARenlaventanadesplegada la funcibn retornará ERROR-FUNC. 67 Sintaxis CODRET ConectarBD2 ( ByVal CadCon BD ByValMostrarErr, CadErrores 1 As String, - As BaseDatos, - As String Argumentos de la función Tabla 17. Argumentos de ConectarBDZ. Tipo de dato Argumento Uso Descripción String CadCon Entrada Cadena conexión de para efectuar el acceso. Los valores típicos son: DSN, UID y PWD BaseDatos BD Salida ldentificador para Base la conexión. de que hace Datos se laa Boolean MostrarErr Entrada Bandera para indicar a la función si se desea o no que ésta despliegue una ventana con el mensaje de error dado por el driver. String CadErrores Salida Texto completo del error arrojado función por la privada DescErrorODBC, tal y como lo reporta el driver de ODBC. Comentarios La funcidn ConectarBD2 es la primer funcidn de la biblioteca de funciones que se debe utilizar alternativamente a ConecfarBD para realizar un acceso a base de datos. Despues de esta funcidn, generalmente se usan las funciones HacerConsultaSQL o EjecufarSQL. AI finalizar las consultas se debe cerrarla conexidn conla funcidn DesconecfarBD. La cadenade conexi6n es de la forma: "DSN=< fuente de dafos>;UlD=<usuario>;PWD=<confraseAa>" podríamostenerporejemplolacadena: "DSN=contable;UID=shiranui;PWD=mai", enlaquese declaraquelafuentededatossellama contable, el usuariocon el quesepide el accesoes shiranui y sucontrasena es mai. Sepuedenagregaralacadenavalorescomo:SERVER, DATABASE y otros, dependiendodel driver de ODBC quese use. Si alguno de los valores no es correcto se desplegara la ventana de conexidn para corregir la informacidn. Es posiblepasar unacadenavacía y esperarque el usuarioproporcionela informacidn correcta usandola ventana que es desplegada porel driver a traves de la funcidn. Códigos de retorno EXlTO-FUNC EXITO-FUNC-CON-INFO ERROR-FUNC 68 7.3 La función HacerConsultaSQL La funci6n HacerConsultaSQL tiene la finalidad de procesar instrucciones de consulta (SELECT'S), y de proporcionar un identificador para el conjunto de resultados generado. Se debe llamar a la funci6n ConectarBD antes que a HacerConsultaSQL. Despues de 6sta funcidn, generalmente se usan lasfuncionesdenavegaci6n de resultados: PriRegConsultaSQL,AntRegConsultaSQL, SigRegConsulfaSQL y UltRegConsultaSQL. Opcionalmente puede se usar la función CerrarlnsfrucSQL despues de procesarlos datos. Sintaxis CODRET HacerConsultaSQL ( BD SQL ByVal CSQL ByValMostrarErr, CadErrores As BaseDatos, As String, AS ConsultaSQL, As String - - ) Argumentos de la funcidn Tabla 18. Argumentos de HacerConsultaSQL. Tipo de dato Argumento Uso Descripción a la que hecho ha se BaseDatos BD Entrada ldentificador base la de datos de conexión String SQL Entrada instrucción Cadena lade SELECT SQL ejecutar. a Regularmente un ConsultaSQL CSQL Salida ldentificador instrucción lade ejecutada. Una vez ejecutada la instrucciónlos valores de sus campos contienen datos válidos sobreel conjunto de resultados como: numero de columnas, de renglones, etc. Boolean MostrarErr Entrada Bandera para indicar a la función si se desea o no que ésta despliegue una ventanacon el mensaje de error dadopor el driver. String CadErrores Salida Texto completo del error arrojado función por la privada DescErrorODBC, tal y como lo reporta el driver de ODBC. Comentarios Se requiere declarar una variable como de tipo ConsultaSQL para que se pase como parametro de salida de la funcidn. La variable se utilizara en otras funciones de la biblioteca ya que identifica al conjunto de resultados obtenidos con la funcibn. Las funciones de navegaci6n de resultados son un ejemplo de las funcionesque usan 6sta variable. Aunque es posible llamar a esta funci6n con instrucciones de escritura no es muy recomendable, pues al tratar la funci6n de determinar los parámetros asociados a un conjunto de resultados se tendran errores. En general la sentencia de escritura se ejecutara bien pero la funci6n regresará EXITO-FUNC-CON-INFO. 69 Códigos de retorno EXITO-FUNC EXITO-FUNC-CON-INFO ERROR-FUNC HANDLE-INVALID0 7.4 La función HacerEscDirectaSQL Esta función se creó con la intención de ejecutar operaciones de escritura hacia la base de datos, comosonsentenciasINSERT,DELETE y UPDATE.Utilizatambiknunavariabledetipo ConsultaSQL pero sólo para recoger algunos datos de inter& como el número de parametros en la sentencia y la cuenta de líneas afectadas en la escritura, esto es, para el INSERT cuantas líneas se insertaron, para el DELETE cuantas se borraron y para el UPDATE cuantas se actualizaron. Puede ejecutar tambi6n sentencias SELECT pero no registrara en la variable toda la informacitm que se obtendría con la funciónHacerConsultaSQL. Sintaxis CODRET HacerEscDirectaSQL ( BD As BaseDatos, SQL ByVal As String, CSQL As ConsultaSQL, ByValMostrarErr, CadErrores As String ) Argumentos de la funci6n Tabla 19. Argumentos de HacerEscDirectaSQL. Descripción Tipodato de Argumento Uso BaseDatos BD Entrada ldentificador de conexión la base datos de a la que se hecho ha String SQL Entrada Cadena de la instrucción ejecutar. SQL aINSERT, DELETE o UPDATE. ConsultaSQL CSQL Salida ldentificador de la instrucción ejecutada. Una vez ejecutada la instrucción, los valores de sus campos contienen datos de interés: número de paremetros y número de líneas afectadas. Boolean MostrarErr Entrada Bandera para indicar a ésta la función si se desea o no que despliegue una ventana con el mensaje de error dado por el driver. String CadErrores Salida Texto completo error del arrojado por la función privada DescErrorODBC,tal y como lo reporta el driver de ODBC. 70 Comentarios Si se utiliza Bsta funcidn para ejecutar una sentencia SELECT se corre el riesgo de perder datos importantes sobre el conjunto de resultados. Esta funci6n es conveniente sblo para sentencias de escritura. Códigos de retorno EXITO-FUNC EXITO-FUNC-CON-INFO ERROR-FUNC HANDLE-INVALID0 La función HacerEscrituraSQL Estafunciónen un principiosepens6usarpara la ejecuci6ndetransaccionesysentencias preparadas,elloimplicaba el uso internodelasfunciones dela API de ODBC SQLPrepare, SQLExec, SQLBindCol, SQLTransac y otras, pero por razones de tiempo no fue posible lograrlo y ahora es una copia dela funci6n HacerEscDirectaSQL. 7.5 La función EjecutarSQL La funci6n EjecutarSQL es mas practica que las funcionesHacerEscrituraSQL y HacerEscDirecta, debido a que no requiere declarar antes una variable de tipo ConsultaSQL. Su uso primordial es paraejecutarcualquierinstrucci6ndeSQLqueno seaSELECT.Estoincluyeinstrucciones INSERT, DELETE, UPDATE, CREATE, REVOKE, GRANT, EXECUTE entre otras. Como no usa la instruccibn,simplemente variablesdetipo ConsulfaSQL noregresainformaci6nrelevantea ejecuta las instrucciones y hace el chequeo de errores, regresando la informacidn de &tos si se presentan. Sintaxis CODRET EjecutarSQL ( BD ByVal SQL ByValMostrarErr, CadErrores As BaseDatos, As S t r i n g , As String - ) Argumentos de la función Tabla 20. Argumentos de EjecutarSQL. Tipo de dato Argumento Uso Descripción BaseDatos BD Entrada ldentificador base de lade datos que la ha hecho se a conexión 71 Uso dato Tipo Argumento de String SQL Boolean MostrarErr String CadErrores Descripción Entrada la Cadena de instrucción SQL a ejecutar. Entrada Bandera para indicar a la función si se desea o no que Bsta despliegue una ventana con el mensaje de error dado porel driver. Salida Texto completo del error arrojado por la función privada DescErrorODBC. tal v como lo reDorta el driver de ODBC. Comentarios La función es capaz de ejecutar cualquier sentencia de SQL reconocida por el driver de ODBC, inclusive sentencias SELECT. El problema con las sentencias SELECT es que no existe ninguna referencia haciael conjunto de resultados generado por lo que no es posible accesar los datos. Códigos de retorno EXITO-FUNC EXITO-FUNC-CON-INFO ERROR-FUNC 7.6 Las funciones de navegación de resultados Las funciones para navegar por el conjunto de resultados son las siguientes: AntRegConsultaSQL-Regresa el cursor al registro anterior del que esta posicionado. SigRegConsulfaSQL-Avanza el cursor al siguiente registro enel conjunto de resultados. PriRegConsultaSQL-Mueve el cursor al primer registrodel conjunto de resultados. UltRegConsulfaSQL-Mueve el cursor al ultimo registrodel conjunto de resultados. Estas funciones se llaman despues de que se ha ejecutado con exit0 la funcidn HacerConsultaSQL para una sentencia SELECT. AI ir moviendo el cursor con dstas funciones los datos del registro sobre el queseposicione el cursorestarandisponiblesdentro de la estructuradeconsulta ConsultaSQLensucampoRegistro.DafosCol.Recordemosque DatosCol es un arreglode cadenas cuya longitud correspondeal número de columnas enel conjunto de resultados y en este arreglo residen los datos. Sintaxis CODRET AntRegConsultaSQL (CSQL AsConsultaSQL, CODRET SigRegConsultaSQL(CSQL AsConsultaSQL, CODRET PriRegConsultaSQL(CSQL As ConsultaSQL, CODRET UltRegConsultaSQL (CSQL AsConsultaSQL, ByVal MostrarErr,CadErr AsString) ByVal MostrarErr,CadErr As String) ByVal MostrarErr,CadErr AsString) ByVal MostrarErr,CadErr AsString) 72 Argumentos de la función Tabla 21. Argumentos de las funciones de navegación de resultados. Tipodato de Argumento ConsultaSQL CSQL Entrada Boolean MostrarErr String CadErr Salida Texto completo Uso Descripcidn ldentificador de la consulta ejecutada. Entrada Bandera para indicar a la funci6n se sidesea o no que ésta despliegue una ventana con el mensaje de error dado por el driver. del error arrojado por la función privada DescErrorODBC, tal y corno lo reporta el driver de ODBC. Comentarios Si el conjunto de resultados esta vacío cualquierade las funciones regresar2 el cbdigo de retorno NO-HAY-MAS-DATOS. Si el cursor esta en el último registro y se hace una llamada a la funcibn SigRegConsultaSQLlafuncibnregresaraNO-HAY-MAS-DATOS.Peronopasa lo mismo si el cursor est& en el primer registro y se llama a la funcibn AntRegConsultaSQL, en este caso la función reconoceal primer registro y deja el cursor en el mismo lugar regresando EXITO-FUNC. Si se ha cerrado el conjunto de resultados con CerrarlnstrucSQL o alguna otra razbn, las funciones regresaran HANDLE-INVALIDO. Códigos de retorno EXITO-FUNC EXITO-FUNC-CON-INFO 0 ERROR-FUNC NO-HAY-MAS-DATOS 0 HANDLE-INVALID0 7.7 La función CerrarInstrucSQL CerrarlnstrucSQL tiene como única funcionalidad liberarlos recursos asociados a las variables de tipo ConsultaSQL. Despues de usaresta funcibn se debe llamar nuevamente a HacerConsultaSQL para obtener una nueva estructura de tipo ConsultaSQL para utilizar. Sintaxis CerrarInstrucSQL (CSQL As ConsultaSQL) 73 Argumentos de la función Tabla 22.Argumentos de CerrarlnstrucSQL. Tipo de Argumento dato Descripcidn Uso ConsultaSQL CSQL Entrada ldentificador de la instrucción SQL desea que secerrar para liberar los recursos asociados. Comentarios El uso de Bsta funcidnes opcional ya que las funciones HacerConsultaSQLy HacerEscDirectaSQL reservan recursos para la sentenciaen forma interna cada vez que son llamadas. Si bien se puede usar CerrarlnstrucSQL al final del procesamiento de sentencias, tarnbien la funcidnDesconectarBD realiza el cierre automAtico de todas las sentencias asociadas con la base de datos que se le pasa como parámetro. Códigos de retorno Esta función no regresa ningún cddigo. 7.8 La función DesconectarBD Esta es la última funcidn que debe llamar una aplicacidn despues de terminar el acceso a los datos deunafuentededatos.Lafuncidn DesconectarBD desconecta a la aplicacidn de la fuente de datos y libera los recursos asociados a esta. Es importante desconectar todas las sesiones con el servidordebasededatosyaquealgunostiposdeellosempleanprocesosparamanejarla conexidn, y si no se presenta la desconexidn por parte del cliente 6stos procesos pueden durar indefinidamente corriendo en elserver-side. Sintaxis DesconectarBD (BD As BaseDatos) Argumentos de lafunción Tabla 23.Argumentos de DesconectarBD. Tipo de dato Argumento Uso Descripcih BaseDatos BD Entrada ldentificador base lade terminar la conexi6n. de datos con la cualdesea se Comentarios Esta funcidn puede tambiBn desplegar los mensajes de error generados por el driver en caso de presentarseerroresduranteladesconexidndelafuente.Estosemanejadeformainterna mediante la funcidn privadaDescErrorODBC con su argumentoMostrarError puesto aTRUE. 74 Códigos de retorno Esta funciónno regresa ningún cddigo de retorno. 7.9 La función ErroresNativos La funcidn ErroresNativos akla los números de error de los errores presentados por el driver o el driver manager a partirdel mensaje de error capturadoen el argumento de salida dela mayoría de las funciones: CadErrores. Esta misma cadena se usa como argumento de entrada de la función ErroresNativos la cual regresa otra cadena conlos números de error capturados, separados por un carhcter 'Y. Estafuncidnpuedeusarseparatraducir los errores del lenguajeenelqueson presentados al espailol, o bien para interpretarlos como mas convenga. Sintaxis String ErroresNativos(ByVa1 MsgError As String) Argumentos de la función Tabla 24. Argumentos de ErroresNativos. Tipo de dato Argumento String MsgError Uso Descripcibn Entrada Cadena con el mensaje de error tal y como lo está en el argumento de salida Cad€mres de las otras funciones. Comentarios Si el mensaje de error es una cadena vacía, la funcidn tambiBn regresa una cadena vacía.Se debe usar como entradael valor del argumento de salidaCadErrores. Códigos de retorno La función regresa una cadena con los números de error separados por"/" 7.10 Las funciones privadas Dentro de la biblioteca Conexion, se crearon dos funciones de carActer privado para uso exclusivo de las otras funciones públicas,una es la que se encargadel manejo de los errores y automatizala presentación delos mismos, Bsta es la funciónDescErrorODBC. La otra es una funcidn que se usa para repetir la consulta asociada a un handle de sentencia, su principal objetivo es el de mover el cursornuevamente al inicio del conjuntoderesultados, la funcidn RepUltimaConsulta. Ambas funciones se describen a continuación. 75 La función privada DescErrorODBC La funcidn DescfrrorODBC se declard como Private con el fin de que sdlo pueda ser utilizada por lasfunciones del mddulo Conexion.bas, puestoqueutilizacomoargumentosdeentrada los handles de ambiente, conexidn y sentencia encapsulados en los tipos de datos descritos en el capítulo anterior. Pero es posible redeclarar 6sta funcidn como Public y usar los handles antes mencionados. Para el de ambiente: BaseDatosENV, para el de conexidn: BaseDatoslD y para el de sentencia: ConsultaSQL.ID. El prototipo de la funcidn es el siguiente: String DescErrorODBC (ByVal rhenv&, ByVal rhdbc&, ByVal rhstmt&, ByVal MostrarErr) As String El ampersand en los argumentos indica que son de tipo Long. El argumento MostrarErr es de tipo Boolean y sirve para indicarle a la funcidn si se desea (TRUE) que despliegue o no (FALSE) una ventana con el mensaje de error generado. La funcidn regresa una cadena con el texto del error dado por el drivery el driver manager. 'I&" La función privada RepUltimaConsulta El objetivo de 6sta funcidn es el de repetir la consulta asociada con la estructuraConsultaSQL. La funcidn utiliza el campo lnstSQL de dicha estructura para volver a ejecutar la instrucci6n SQL. El prototipo de la funcidn esel siguiente: CODRET RepUltimaConsulta(CSQLAs ConsultaSQL, ByVal MostrarErr, CadErrS) El signo "$" en el argumento CadErr indica que es de tipoString. El argumento MostrarErr cumple el mismo objetivo que en DescErrorODBC. La funcidn regresa los c6digos de retorno conocidos: y HANDLE-INVALIDO. El uso que se EXITO-FUNC, EXITO-FUNC-CON-INFO, ERROR-FUNC le dadentrodelasfuncionespúblicases el deposicionar el cursornuevamente al inicio del conjunto de resultados, no en el primer registro, sino uno antes de este. Por ejemplo la funcidn PriRegConsultaSQL primero llama aRepUltimaConsultay despues a la funcidn de la API de ODBC SQLFetch, con lo que el cursor queda en el primer registrodel conjunto de resultados. 8 USO de las funciones En este capítulosever2nenaccidnlasfuncionesde la bibliotecadefuncionesmediante una aplicacidn Visual Basic de ejemplo. Se describir2 brevemente el esquema de uso de las mismas en una aplicacidn, como se hizo con las funciones de la API de ODBC. 8.1 Un ejemplo sencillo En esta parte se dar2 unlistado de una aplicacidn VisualBasic en la cual se hace la conexión a una base de datos, se crea una tabla, se insertan dos registros en la misma, se hace una consulta en ella y sedespliegan los resultados.Posteriormenteseeliminalatabla,secierralaconexidn y termina el programa. Supongamosqueexisteunabase de datosdeMicrosoftAccessllamada coneccion. En ella crearemos la tabla tgrueba con los siguientes campos: id, datol y dato2. El primero de tipo numkrico que ser2 la llave primaria y los dos restantes de tipo texto con una longitud de 20 y 30 caracteres respectivamente. La instruccidn SQL de la Microsoft Jet database engine para crear la tabla en la base de datos de Access es: CREATE TABLE t-prueba ( i d INTEGERCONSTRAINT d a t o l TEXT ( 2 0 ) , d a t o 2 TEXT ( 3 0 ) c o n s t l PRIMARY KEY, ); Despues de creada la tabla insertamos dos registros con las siguientes instrucciones INSERT: INSERT INTO t-prueba INSERT INTO t-prueba VALUES ( 1 , ' p r u e b a l ' , VALUES ( 2 , ' p r u e b a 2 ' , 77 'texto pruebal'); 'texto prueba2'); 7% La consulta esla siguiente: SELECT * FROM t-prueba WHERE id= 1; Para eliminar la tabla hacemos: DROP TABLE t-prueba; Con lo anterior ya estamos en condiciones de crear nuestra aplicaci6n en VisualBasic usando las funciones de la biblioteca. Todo el código lo pondremos dentro del metodo Click de una forma de VisualBasicquepordefaultsera Forml. En la formaincluiremostrescajasdetextopara el despliegue delos datos. Tambikn les dejaremos sus nombres por omisibn: Textl, Text2 y Text3. El códigoquesemuestraacontinuaci6nhaceusodelassiguientesfunciones:ConectarBD2, EjecutarSQL, HacerEscDirectaSQL, HacerConsultaSQL, SigRegConsultaSQL, CerrarlnstrucSQLy DesconectarSD, las cuales son la mayoría de las funciones de la biblioteca. El c6digo es pues el por simplicidad. mostrado enel Listado 8. El manejo de errores se omite Listado 8. Ejemplo de uso de las funciones de la biblioteca Conexion.bas. Private Sub Form-Click() 'Declaramos las variables necesarias DimBaseAsBaseDatos'Identificadordelabasede datos Dim ConSQL As ConsultaSQL 'Identificador de la consultaSQL Dim CodRetAsInteger'Código de retorno de lasfunciones Dim Error As String 'Cadenas de error generadas Dim sql As String 'Instrucción SQL 'Se realiza la conexión y obtenemos el identificador de base de datos CodRet = ConectarBDZ("DSN=coneccion;", Base, True, Error) 'Se crea la tabla de ejemplo sql = "CREATE TABLE t-prueba ( " sql = sql + "id INTEGER CONSTRAINT constl PRIMARY KEY," s q l = sql + "dato1 TEXT (20), " s q l = sql + "dato2 TEXT (30)" sql = s q l + CodRet = EjecutarSQL(Base, sql, True, Error) ' I ) ;I' 'Se insertan dos registros: 'Uno con HacerEscDirecta y el otro con EjecutarSQL sql = "INSERT INTO t-prueba VALUES (1, 'pruebal' , 'texto pruebal' ) ;" CodRet = HacerEscDirectaSQL(Base, sql, ConSQL, True, Error) Debug.PrintConSQL.NumCols'Númerodeparámetros Debug.Print ConSQL.NumRegs 'Número de registros afectados sql = "INSERT INTO t prueba VALUES (2, 'prueba2', ' texto prueba2 ' ) ;" CodRet = EjecutarSQLIBase, sql, True, Error) 'Se realliza la consultay obtenemos su identificador en ConSQL * FROM t-prueba WHERE id = 1 ; " CodRet = HacerConsultaSQL(Base, sql, ConSQL, True, Error) Debug.Print ConSQL.NumCols 'Número de columnas obtenidas Debug.Print ConSQL.NumRegs 'Número de registros leidos sql = "SELECT 'Se obtiene la información CodRet = SigRegConsultaSQL (ConSQL, True, Error) 'Se despliegan los datos: nombre de columna y contenido 79 Text1 Text2 Text3 = = = ConSQL.NomCol(1) + " : " + ConSQL.Registro.DatosCol(1) + ConSQL.Registro.DatosCol(2) ConSQL.NomCol(2) + + ConSQL.Registro.DatosCol(3) ConSQL.NomCol(3) + 'I:" 'I:" 'Se cierra la instrucción actual para indicarle al driver que la 'tabla referenciada en la sentencia identificada por ConSQL no ya 'está siendo usaday así poder eliminarlacon DROP TABLE. De lo 'contrario el driver emite un mensaje de error indicando que la uso 'tabla aún está en CerrarInstrucSQL ConSQL 'Eliminamos la tabla sql = "DROP TABLE t-prueba;" CodRet = EjecutarSQL(Base, sql, True, Error) 'Se cierra la conexión DesconectarBD Base End Sub Obs6rvese lo sencilloy compacto del cddigo y comparese con la versatilidad que 6ste tiene. En un grupo reducido de líneas logramos hacer una conexidn con una basede datos, creamos una tabla y le insertamos registros, despues leímoslos registros y los presentamos, para finalmente eliminar la tabla de la base de datos y cerrar la conexidn. Si se hubieran usado las funcionesde la API de ODBC directamentehabríanresultadoquiz3 mas de dospaginas de cddigo,sinmencionar lo complicado que sería. Algoimportantequehayqueresaltar del cddigoeslanecesidaddecerrar la instruccidnde consulta que asocia a la tabla tdrueba mediante la funcidn CerrarlnstrucSQL, pues mientras el driver mantenga handles activos asociados con una tabla 6ste interpreta que dicha tabla aún se encuentra en uso, por loquenoesposiblerealizarningunaoperacidnDDL(DataDefinition Language) que modifique la estructura dela tabla asociada. Para demostrar esto podemos eliminar la siguiente líneadel cddigo: CerrarInstrucSQL ConSQL AI ejecutarse la sentencia DROP TABLE mediante la funcidn EjecutarSQL siguiente, con su opcidn de mostrar error puesta a verdadero,el driver de Microsoft Access desplegarael mensaje de error que semuestraen la figura 6 . Ndteseque el mensajedeerrorcontienetodos los elementos discutidos en el apartado 6 del capítulo 3, ademas de un dato adicional que es el número de error con el que el driver de Microsoft Access clasifica aese error, tal inforrnacidn tiene la leyenda "Error nativo:" seguido porel número de error. El argumento de salida CadError contiene exactamente el mismo texto que el mostrado por la ventana de error. Para aislar los números de error úsese la función ErroresNativos de la bibliotecade funciones. Figura 6. Mensaje de error del driver de Microsoft Access. 80 Si se desea llevar a la practica el ejemplo aquí estudiado se debe crear un proyecto nuevo de VisualBasic y dibujar una forma que por omisi6n debera llamarse Forml , despues a la forma se le agregan tres cajas de texto o Text Boxes sin cambiarles el nombre. En el metodo Click de Forml se introduce todoel c6digo arriba listado. Para hacer uso de las funcionesde la biblioteca se deben añadir al proyecto dos mddulosmuy importantes: ODBC32.basy Conexion.bas, el primero tiene las declaracionesdelasfunciones de laAPI de ODBC quesonutilizadasdentro del m6dulo Conexion.bas, el cual cuenta con los prototipos de las funciones de la biblioteca de funciones. Los listados de los m6dulos ODBC32.basy Conexion.bas se muestranen los apkndices Ay B. 8.2 Esquema de uso de las funciones Dentro del c6digo en el apartado anterior se puede adivinar cual es el orden de llamada a las funciones de la biblioteca de funciones. A continuaci6n se describira rapidamente el formato que seguiríaunaaplicaci6nVisualBasicqueutilicelasfunciones del m6dulo CONEXION. Este esquema es parecido en cuanto a orden al mostrado en la Figura 5 del capitulo 4, referente a las funciones de la API de ODBC. En general el formato de uso de las funciones de la API de ODBC es la combinaci6nde los esquemas mostrados enlos capítulos 3 y4. Se nota a simple vistaque el esquema de uso de funciones que ahora nos ocupa resulta mas compacto. La Figura7 muestra la secuencia que normalmente se sigue al usar las funciones de la biblioteca de funciones para la conexi6n a basesde datos abiertas. ConectarBD ConectarBD2 + + + AntRegConsultaSQL SigRegConsultaSQL PriRegConsultaSQL UltRegConsultaSQL + + v HacerEscDirectaSQL HacerEscrituraSQL HacerConsultaSQL EjecutarSQL , * ErroresNativos - + + v CerrarInstrucSQL J DesconectarBD Figura 7. Esquema de uso delas funciones del m6dulo CONEXION. 81 AI igual que como sucede con API, la lo primero que se requiere es realizar la conexi6n con la base de datos, esto se logra con ConectarBD o ConectarBD2, despues se debe elegir entre hacer una consulta con HacerConsultaSQL, realizar una operaci6n de escritura con HacerEscDirectaSQL o HacerEscrituraSQL,o cualquier otro tipo de instrucci6nSQL con EjecutarSQL. Si se decidi6 hacer una consulta lo que sigue es usar cualesquiera de las funciones de navegaci6n de resultados, es decir, PriRegConsultaSQL, SigRegConsultaSQL,AntRegConsultaSQL y UltRegConsultaSQL. Es posiblequesenecesiteliberarrecursosasociadosa una consultacon CerrarlnstrucSQL. Finalmentese libera la conexi6n con DesconectarBD. Dentro de todo el procesosepueden obtener los númerosdeerrorcuando la aplicaci6n setopeconestosusando la funcibn ErroresNativos. 8.3 Nuevamente el manejo de errores AI igual que las funciones de ODBC, las de la biblioteca de funciones tambien cuentancon c6digos de retorno para indicar la forma en que se ejecut6 la funcibn, y como ya se vio en el capítulo anterior, cada funcibn de la biblioteca cuentacon un conjunto fijo de c6digos de retorno. Sabiendo esto ya conocemos así los c6digos que se pueden esperar para cada funci6n (para las funciones que regresan c6digos de retorno), y como se hizo con las funciones de ODBC tambien en dste caso se puede realizar un manejo sencillo de errores, usando bloques I[ Then o bloques Select, Case. El fragmento de c6digo mostradoen el Listado 9 dará mayor claridad a las palabras. Listado 9. Ejemplo de manejo de errores. Dim Base As BaseDatos Dim CodRet As Integer Dim CadConAs String Dim ErroresAs Strin CadCon = "DSN=coneccion;UID=ukyo;PWD=shampoo;" CodRet = ConectarBD2 (CadCon, Base, True, Errores) if CodRet <> EXITO-FUNC Then su propio mensajede error 'La función despliega además 'pues la bandera mostrar error esta puesta a TRUE MsgBox "Error al intentar conectar ala base de datos" Exit Sub Else MsgBox "La conexión se realizó con éxito " End I f Esta es la forma más sencilla de tratar los errores,puessiemprepodemosusarlabandera MostrarError en las funciones que la tengan para que la funci6n despliegueel mensaje de erroren forma automática, y probar los c6digos de retorno para bifurcar la ejecuci6n del programa como más convenga. Silo que se desea es una revisidn más exhaustiva podemos usar un bloqueSelect Case en lugar del If Then y probar todos los casos. Para un análisismás detallado de los errores contamos tambiencon la funci6n ErroresNativos,con la que podemos aislarlos números de error y tomar acci6n para cada caso, sabiendo de antemano que error implica cada número conla documentacidn del driver deODBC que se utilice. Tomemos 82 por ejemplo el error de la Figura 6. El error nativo del driver de Microsoft Access que identifica la condici6n de que una tabla está siendo usada por otra personao proceso es el error que tiene el número -1304. Sabiendo esto podemos hacer: Dim NumerosErrorAs String NumerosError = ErroresNativos(MsgError) If InStr(NumerosError, "-1304") > O Then o proceso" MsgBox "La tabla está siendo usada por otra persona 'Tomar acción respecto al error. Por ejemplose puede 'cerrar la instrucción con CerrarInstrucSQL Exit Sub End If Lo que es más amigable que el mensaje de error en ingles mostrado en la Figura 6, ademas, permite tomar decisiones más precisas que simplemente usando los c6digos de retorno. Para el listadoanteriorpodemos,porejemplo,cerrar la instrucci6nqueinvolucrea la tabla y probar nuevamente con la instrucci6n que gener6 el error. PARTE 111. JDBC O Introducción a JDBC O Conexión JDBC a bases de datos 9 Introducción a JDBC La Java Database Connectivity o JDBC, es junto con Java de Sun Microsystems uno de los hitos en herramientasde desarrollo mas innovadores en los tiemposde la red de redes o Internet. JDBC es al igual que ODBC una API de desarrollo, pero especialmente creada parael lenguaje Java. En los capítulos que forman el resto de este trabajo se hara un breve estudio de la API de JDBC. El objetivo de 6sta tercera parte es el de introducir la API de JDBC y proporcionar los elementos mínimos para crear aplicaciones Java capaces de conectarse a bases de datos abiertas. Para este capítulo se hacela suposición de que el lector tiene algunos conocimientos sobre el lenguaje Java. 9.1 Que es JDBC JDBC es una API de Java que permite ejecutar instrucciones SQL (Structured Query Language: Lenguajeestructuradodeconsultas),queesunlenguaje de alto nivel paracrear,manipular, examinar y gestionar bases de datos relacionales. Consiste de un conjunto de clases e interfaces escritasen el lenguaje de programaciónJava.Comoveremosmasadelanteparaque una aplicación pueda hacer operaciones en una base de datos, ha de tener el correspondiente Driver que conecte la aplicaci6n con esta. Así puesla API de JDBC es basicamente un paquetede JAVA (javasql) que contiene un conjunto de clases e interfaces escritas en JAVA. JDBC proporciona una APIcomúnparadesarrolladoresdeherramientasdebasesdedatosyhaceposibleescribir aplicaciones de basesde datos usando Java puro. Con JDBC, es facil enviar instrucciones SQL a virtualmente cualquier base de datos relacional. En otras palabras, con la API de JDBC, no es necesario escribir un programa que accese a una base dedatosSybase,otroqueacceseaunabase de datosOracle,otroparaunabasededatos Informix, y así consecutivamente. Uno puede escribirun solo programa ,usandola API de JDBC, y el programa sera capaz de enviar instrucciones de SQL a las bases de datos apropiadas. Y, con una aplicación escrita en el lenguaje de programación Java, tampoco hay que preocuparse de a5 86 escribir diferentes aplicaciones para que corran en distintas plataformas. La combinaci6n de Java y JDBC permite al programador escribirel programa una vez y correrlo practicamente donde sea. Es posible correr un programa Java sobre cualquier plataforma que lo habilite aún sin recompilar ese programa. El lenguaje Java esta completamente especificadoy, por definicibn, una plataforma con Java habilitado debe soportar un grupo conocido de librerías principales. Una de esas librerías es JDBC, la cual se puede pensar comouna versi6n Java deODBC, y es por sí misma un estandar en crecimiento. Los vendedores de basesde datos se estan ocupando de crear puentes de JDBC a sus sistemas particulares. JavaSoft tambien provee un driver de puente que comunica a JDBC conODBC,permitiendo la comunicaci6nconbases de datospropietariasquenocuentancon drivers nativosJDBC. El uso de Java en conjuncibn conJDBC proporciona una verdadera soluci6n portable al escribir aplicaciones de bases de datos. Java, siendo robusto, seguro, facil de usar, fhcil de entender, y automaticamente descargable en una red, es un excelente lenguaje para las aplicaciones de bases de datos. Senecesitabauna forma de que las aplicaciones Java se comunicaran con las distintas bases de datos existentes. JDBC es el mecanismo para hacerlo. JDBC extiende las capacidades de Java. Por ejemplo, con Java y la API de JDBC, es posible publicar una pagina Web que contenga un applet que use informaci6n obtenida de una base de datos remota. O una empresa que use JDBC para conectar a todos sus empleados (aún si ellos usan un conglomerado de sistemas Windows, Macintosh y UNIX) a una o mas bases de datos internas por mediode una intranet. Qué es lo que hace JDBC De forma simple, JDBChace posible tres cosas: establecer una conexi6n con una base de datos, que puede ser remota o no. enviar instrucciones SQL a la base de datos. procesar los resultados obtenidos de la base de datos. El siguiente fragmento de cddigo da un ejemplo besico de estos tres pasos: Connection con = DriverManager.getConnection( "jdbc:odbc:base", "login", "password") ; Statement stmt = con.createStatement0; Resultset rs = stmt .executeQuery ("SELECTb, a, c FROM Tabla"); while (rs.next0) { int x = rs.getInt ("a"); String S = rs.getString("b"); float f = rs.getFloat ("c"); } JDBC es una API de bajo nivel y es la base de APls de alto nivel JDBC es una interfaz de "nivel bajo", lo que quiere decir que es usada para invocar (o llamar) comandos SQL directamente. Trabaja muy bien en este aspecto y es m& facil de usar que otras APls de conectividad a bases de datos, pero fue disefiada tambien para ser la base sobre la cual a7 construir interfaces de alto nivel y herramientas. Una interfaz de alto nivel es "amigable al usuario", y utiliza una API mas entendible o conveniente la cual se traduce detras del escenario en una interfaz de nivel bajo tal como JDBC. AI tiempo de escribir esto, se encuentran en desarrollo dos APls de alto nivel basadas en JDBC: 1. UnSQLintegradoparaJava.LosDBMS'simplementanSQL,unlenguajedisefiado especificamente para usarse con bases de datos. JDBC requiere que las sentencias de SQL se pasen como cadenas a los metodos de Java. Un preprocesador integrado de SQL permitire al programador mezclar sentencias SQL directamente con Java: por ejemplo, una variable Java puede usarse en una sentencia SQL para recibir o proporcionar valores de SQL. El preprocesador integrado de SQL traslada luego esta mezcla de Java/SQL a Java con llamadas de JDBC. 2. Un mapeo directo de tablas de una base de datos relaciona1 a clases de Java. JavaSoft y otros tienen planes de llevar a cabo esto. En 6ste mapeo "Objeto/Relacibn", cada línea de una tabla viene a ser una instancia de la clase, y cada valor de columna corresponde aun atributo de esa instancia. Los programadores pueden entonces operar directamente con objetos de Java; las llamadas SQL requeridas para extraer y almacenar datos se generan automaticamente "bajo la cubierta." Tambi6n se proporcionan mapeos mas sofisticados, por ejemplo, cuando renglones de tablas múltiples se combinan en una clase de Java. Como el inter& en JDBC ha crecido, mas desarrolladores trabajan con herramientas basadas en JDBC para construir programas mas facilmente. Los programadores tambi6n escriben aplicaciones que hacen el acceso a las bases de datos mas sencillo para el usuario final. Por ejemplo, una aplicaci6n puede presentar un menú de tareas de bases de datos de las cuales escoger. Despues de que una tarea es seleccionada, la aplicacidn presenta los cuadros de texto para llenarlos con la informaci6nnecesariaparallevaracabolatareaseleccionada.Conlaentradarequerida ya ingresada, la aplicaci6n entonces invoca autometicamente los comandos SQL necesarios. Con la ayuda de una aplicaci6n como 6sta, los usuarios pueden hacer operaciones con bases de datos aún cuando tengan pocoo ningún conocimiento de la sintaxis de SQL. 9.2 JDBC versus ODBC y otras APls En &te punto, la API de ODBC (Open Database Connectivity) de Microsoft es probablemente la interfaz de programaci6n de aplicaciones mas ampliamente usada parael acceso a bases de datos relacionales. Esta ofrece la habilidad de conectarse a casi todas las bases de datos en casi todas las plataformas. Nos preguntaremos que significado tiene entonces JDBC si ya existe una interfaz popular que supuestamente hace lo mismo, es decir, porque simplemente no usar ODBC desde Java? La respuesta es que si se puede usar ODBC desde Java, pero es mejor con la ayuda de JDBC en la forma de un driver puente JDBC-ODBC. Estos drivers traducen las llamadas de JDBC a ODBC permitiendo comunicarse con bases de datos propietarias que no tienen ni idea de que existe Java. De esta manera por ejemplo podemos trabajar con una base de datos Access de Microsoft que usa ODBC, con el lenguaje Java. La pregunta ahora viene a ser, "Porquk es necesario JDBC?" Hay varias respuestas a 6sta pregunta: 1. ODBC no es apropiado para usarse directamente con Java porque utiliza una interfaz tipo C.LasllamadasdesdeJavaac6digo C tienenvariasdesventajasenlaseguridad,la puesta en prhctica, robustez, y en la portabilidad autometica de las aplicaciones. 88 2. Unatraduccidn literal de la ODBC C API aunaJavaAPIpodríanoserdeseable.Por ejemplo, Java no tiene apuntadores, y ODBC hace un copioso uso de ellos, incluyendo el notoriamente propenso a error apuntador generico "void *". Se puede pensar en JDBC como un ODBC traducido a una interfaz orientada a objeto que es mas natural para los programadores de Java. 3. ODBC es difícil de aprender. Este combina caracteristicas simples y avanzadas, y tiene opciones complejas aún para consultas simples. JDBC, por otro lado, fue disenado para mantenersimple lo simplemientraspermitecapacidades mas avanzadascuandoes necesario. 4. UnaJavaAPIcomo JDBC esviabledeformaquesepuedaobtener una soluci6ncon "Java puro". Cuando se usa ODBC, el ODBC driver manager y los drivers se deben instalar manualmente en cada mequina cliente. Como el driver de JDBC esta escrito completamente en Java,el cddigo JDBC es automaticamente instalable, portable, y seguro en todas las plataformas Java desde computadoras de red hasta mainframes. En resumen, la JDBC API es una interfaz Java natural paralos conceptos y abstracciones basicas del SQL. Esta construida sobrelos principios usados paraODBC, de forma que los programadores familiarizados con ODBC encontraran muy facil de aprender JDBC. JDBC retiene las caracteristicasbasicas de diseiio deODBC; de hecho,ambasinterfacesestanbasadasen la NOpen SQL CLI (Call Level Interface). La gran diferencia es que JDBC se estructura y refuerza sobre el estilo y virtudes de Java,y, por supuesto, es facil de usar. Recientemente, Microsoft ha introducido nuevas APls mas alla de ODBC: RDO, ADO, y OLE DB. Estos diseiios se mueven en la misma direcci6n que JDBC de muchas maneras, esto es, en ser una interfaz de base de datos orientada a objetos basadaen clases que puede ser implementada sobre ODBC. Sin embargo, no se ha visto funcionalidad que imponga a algunade 6stas interfaces comounaalternativaaODBC,especialmenteconunmercadodedrivers de ODBCbien establecido. La mayoría de ellos representan un fino disfraz para ODBC. No se esta diciendo que JDBC no necesite evolucionarde su liberacidn inicial; sin embargo, posiblementela mayoría de las nuevas funcionalidades radicaran enAPls de alto nivel tales como los mapeos objetolentidad y el SQL integrado mencionados antes. 9.3 La arquitectura de JDBC Como se 'mencion6 anteriormente, JDBC es una API de bajo nivel y es la base para APls de alto nivel: los desarrolladoresusaranlasclaseseinterfaces de la APIdeJDBCparahacerlas transaccionesnecesariascon el DBMS, mientras el fabricante de driversutiliza el JDBCpara modelarlos de acuerdo a &te. dos capas principales: la API de JDBC que soporta las En detalle JDBC consiste de comunicaciones entre la aplicaci6n y el Administrador de JDBC; y la API del JDBC Driver que soporta las comunicaciones entreel Administrador de JDBC y el driver. El Administrador maneja la comunicaci6n con múltiples drivers de diferentes tipos, desde interfaces directas enJavahasta drivers de red y drivers basados en ODBC. Ver la Figura 8. 89 t .......................................................................... JDBC A P I parte de JDBC (paquete:java.sql.*) ................................. ......................................... JDBC Driver A P I Figura 8. Arquitectura de JDBC. Acceso de la API JDBC a las bases de datos La API de JDBC soporta dos modelos distintos de acceso a las bases de datos: 0 0 Modelodedoscapas(two-tier). Modelodetrescapas(three-tier). Modelo de dos capas (two-tier) En el modelo de dos capas (Figura 9), un applet de Java o aplicacibn interactúa directamente con la base de datos. Esto requiere de un driver de JDBC que se pueda comunicar con el sistema manejador de la base de datos en particular que esta siendo accesada. Este driver estara instalado en el sistema local. Las sentencias de SQL de los usuarios son entregadas a la base de datos, y los resultados de esas sentencias son enviados de regreso al usuario. La base de datos puede localizarse en otra mequina a la cual el usuario se conecta vía una red. Esto se conoce como una configuración Cliente/Servidor, con la mequina del usuario como el cliente,y la maquina que tiene la base de datos comoel servidor. El programa cliente envia instrucciones SQL a la base de datos, y esta las procesa y envía los resultados de vuelta al usuario. La red puede ser una intranet, la cual, por ejemplo, conecta alos empleados de una corporación,o bien puede ser la Internet. 90 1 - I1 I Aplicación Java Máquina Cliente 1 DBMS-protocolo propietario 1- Servidordebasededatos Figura 9. Modelo de dos capas, Modelo de tres capas (three-tier) En el modelo de tres capas (Figura IO) los , comandos son enviados a un "middle tier" o capa intermedia de servicios, el cual luego envía las sentencias SQL a la base de datos. La base de datos procesa las sentencias SQL y envía los resultados de vuelta a la capa intermedia, quien despues los envía al usuario. El modelo de tres capas resulta muy atractivo ya que la parte media o middle tier hace posible mantenerun control sobre los accesos y los tipos de operaciones que se pueden hacer en la base de datos. Otra ventaja de la existencia del middle tier es que el usuario puede emplear una API de alto nivel de uso fdcil que es trasladada por la capa intermedia en las llamadas de bajo nivel apropiadas. Finalmente, en muchos casos la arquitectura de trescapas puede proporcionar ventajas de rendimiento,ya que los drivers JDBC para conectarse con la base de datos, nohan de residir enla maquina cliente. Mtiquina Cliente HTML browser Llamadas aHTTP, Rh41, CORBA Servidor de Adicaci6n (Java) I L ~ kServidor ~ ~ i ~ ~ DBMS-protocolo propietario DBMS I Servidorbase de datos de I Figura I O . Modelo de trescapas. Hasta ahora el middle tier se ha escrito típicamente en lenguajes como C o C++, que ofrecen un alto rendimiento. Sin embargo, con la introducci6n de compiladores de optimizaci6n que traducen el cddigo compilado Java en c6digo de maquina específico y eficiente, se esta volviendo practico el implementar la capa intermediaen Java. Esto es una gran adicibn, haciendo posible tomar ventaja de la robustez de Java, los multihilos de ejecucibn, y las características de seguridad. JDBC es importante para permitirel acceso a basesde datos desdeun middle tier Java. 10 Conexión JDBC a bases de datos En Bste capítulo conoceremos las clases del paquete javasql. Estudiaremos las clases mínimas y para realizar una aplicacidn Java que sea capaz de conectarse a una base de datos existente realizar la gestidn de la misma. Se veran a su vez los metodos mas importantes de dstas clases que intervienen directamente en la conexidn con una fuente de datos y la ejecucidn de sentencias de SQL. La aplicacidn se desarrollara y probara en este mismo capítulo usando las herramientas del Java DevelopmentKit (JDK) proporcionado en forma gratuita por Sun Microsystems. 10,l Las clases del paquete java.sql AI igual que como sucedid con ODBC, la API de JDBC contiene un grupo de clases para el trabajo de conexidn a la fuente de datos, otro grupo para la ejecucidn de comandos, otro para obtener características de drivers y del conjunto de resultados, y clases para el manejo de errores. Todas &stas clases estan contenidas en el paquete java.sg/ dentro del archivoc/asses.zipen del directorio lib de jdk. Como es común en Java, no todas las clases de un paquete se utilizan igual, pues algunas son interfaces, otras son objetos y el resto son excepciones para el manejo de errores, y la API de JDBC no es diferente respecto a esto. La API de JDBC para la versidn de JDK 1.1.6 (al tiempo de escribir esto Sun anunciaba la versidn 2.0 de JDBC que viene con el JDK 1.2) cuenta con un total de ocho interfaces, seis objetos y tres excepciones documentadas el enpaquete. Como el tema de Bste trabajo no es Java ensí sino JDBC, no se profundizara en cuanto alos conceptos de Interfaz, Objeto y Excepcidn de Java, tampoco se hablara de los modificadores public, private, abstract y otros.Seentiendeque el lectortienenocionessobreesto. A continuacidn sehaceunabreve descripcidn de las clases de la API de JDBC. 91 92 Interfaces de JDBC El paquete javasql proporciona varias interfaces para lograr la conexidn con la base de datos y ejecutarsentenciasdeSQL.ConlasinterfacesdelaAPIdeJDBCsepuedenejecutar instrucciones normales de SQL, instrucciones dinarnicas deSQL y procedimientos almacenados o storedprocedures. Las interfaces deJDBC son las siguientes: CallableStaternent La interfaz CallableStaternent proporciona metodos para llamar stored procedures que regresan valores de salida. El objeto CallableStaternent hereda el objeto Preparedstatement, pero a su vez agrega metodos para registrar parametros para que sean parametros de salida y proporciona otros metodos para quelos parametros sean devueltos desde el procedimiento almacenado. Connection La interfaz Connection es el objeto que proporciona a la aplicacidn Java una conexidn a la base de datos. Este objeto puede emplearse para crear todos los distintos objetos Statement para ejecutar instrucciones de SQL y procedimientos almacenados. Tambikn permite establecer las propiedades de transaccidn para la conexidn. DatabaseMetaData La interfaz DatabaseMetaData proporciona diversos metodos para obtener informacidn sobre la base de datos. La interfaz proporciona mktodos para obtener el listado de las tablas de una base de datos especifica,asi como las llaves primarias, las columnasy otros varios tipos de informacidn para las tablas especificadas. Driver El objeto de la interfazDriver es un objetoDriver para una base de datos especifica proporcionado por el distribuidor de JDBC. Contiene informacidn específica sobre la conexidn de la aplicacidn Java.Proporcionatambieninformacidnsobrelabasededatos(porejemplo,informacidndela version). Preparedstatement La interfaz PreparedStatement permite ejecutar instrucciones dinarnicasde SQL y procedimientos almacenados. Las instrucciones dinarnicas de SQL difieren de las instrucciones normales de SQL porque en las instrucciones dinarnicas no se conocen los valores al momento de la creacidn. La interfazpermiteestablecer los diversosparametrosdeinstruccionesdinarnicasconvaloresde datos especificados. Resultset Lainterfaz Resultset es el objetoquesecrea y se utilizaparaobtenerinformacidndeuna instruccidnSelectde SQL. Estainstruccidnretornauncursorqueesutilizadoporlainterfaz Resultset para navegar a traves delos resultados retornados por la instruccidn Select. Proporciona varios metodos para obtener informacidn de las diferentes columnas contenidas en el cursor. 93 ResultSetMetaData La interfaz ResultSetMetaDafa permiteobtenerinformacidnsobreunconjuntoderesultados retornado. El objeto ResultSetMetaData secreaapartirdeunobjeto ResultSet yproporciona informacidn especlfica a ese objeto. Permite obtener, de un conjunto de resultados, el número de columnas, los nombresytiposde las columnas, y otrainformacidnpertenecientealobjeto ResultSet retornado. Statement La interfaz Statement secreaapartirdelobjeto Connection y se puedeusarparaejecutar instrucciones SQL estandar y procedimientos almacenados. El objeto proporciona dos metodos principales: executeQuery() y executeUpdate(). Estos metodos permiten ejecutar consultas de SQL y actualizaciones de SQL. El rnetodo executeQuery() retornara un objeto ResultSet. Este objeto es el ancestro para las interfacesPreparedStatementy CallableStatement. Objetos de JDBC El paquete java.sq/ tambien proporciona algunos objetos que pueden usarse en las aplicaciones Java. La mayoría de los objetos se emplean para proporcionar a Java algunos de los tipos de datos específicos para base de datos disponibles en la mayoría de las bases de datos. Los objetos de la API de JDBC se enlistan enseguida: Date El objeto Date puede aceptar valores de tipoDate de base de datos, es heredadodel objeto Date normal de Java, pero proporciona metodos para accesar los distintos valores dentro del objeto Date. DriverManager El objeto DriverManager proporciona otra forma de realizar una conexidn a la base de datos. El objeto se usa principalmente para administrar objetosDriver de JDBC y puede utilizarse para crear una conexidn a la base de datos. Proporciona varios metodos para el registro de controladores, obtencidn de conexiones y envío de informacidn al flujo de salida de la base de datos. DríverPropertylnfo El objeto DriverPropedylnfo esutilizadoprincipalmentepor los programadoresavanzadospara manejar propiedades específicas de un objeto Driver. Sdlo deben emplearlo los desarrolladores que esten familiarizados conel funcionamiento de controladores de bases de datos. Time El objeto Time es heredado del objeto Date de Java. Proporciona varios metodos para obtener y asignar valores del objeto. Se puede emplear para obtener valores de datos Time de la base de datos. 94 Timestamp El objeto Timestamp puede utilizarse para obtener valores de datos de la base de datos que son deltipo Timestamp. El objetoproporcionavariosmetodosparacomparar los valores de dos diferentes objetosTimestamp. TY Pes El objeto Types contiene una lista de valores enteros predefinidos que identifican cada uno de los diferentes tipos de datos disponibles para uso en aplicaciones JDBC. Los valores se utilizan en diferentes mktodos de la API de JDBC para especificar o identificar los tipos de valores de datos particulares. Excepciones de JDBC CuandoocurreunerrorenJava,selanzaunaexcepci6n.Cualquierexcepci6nquelancen los metodosJavadebeser"atrapada"por el usuario.LaAPIdeJDBCcontienetresnuevas excepciones que pueden atraparse identificando varios errores enla ejecuci6n de metodos JDBC. A continuaci6n se enumeran las tres excepciones deJBDC: DataTruncation La excepci6n DafaTruncation se lanzasiempreque JDBC trunca un valordedatosenforma inesperada. La excepción proporciona metodos para obtener informaci6n sobre el valor de datos que fue truncado así como para obtener informacibn sobre el error de truncamiento. SQLException Laexcepcidn SQLException eslanzadapor casi todos los metodosen la APIdeJDBC.Esta excepci6n proporciona varios metodos para obtener informaci6n sobre el error y el estado actual de la transaccidn SQL. SQLWarning Laexcepcidn SQLWarning se generacuando la basededatosemiteunaadvertencia.La advertencia se envia silenciosamente al objeto que la caus6. 10.2 Conexión con la base de datos Existen dos formas de lograr conectarseuna a base de datos. Una forma es con la interfazDriver y la otra escon el objeto DriverManager de JDBC. Para este efecto utilizaremos la base de datos de Access de los ejemplos de ODBC, cuyo nombreDSN es coneccion. Veamos en primer lugar como lo hace la interfaz Driver y despues veremoslo propio conel objeto DriverManager. 95 La interfaz Driver La interfaz Driver ofrece varios metodos para obtener informaci6n sobre el controlador actual de la base de datos, entre ellos el metodo connect(), con el que se crea un objeto Connection que se puede usar para accesar a la base de datos. La interfaz Driver proporciona los metodos siguientes: Tabla 25. Métodos de la interfaz Driver. Método Descripci6n acceptsURLRegresaunvalorbooleanoqueindicasieldriverpuedeconectarseal especificado. connect URL Crea una conexión a la base de datos y regresa un objeto Connection para usarse en la aplicación. getMajorVersion Obtiene la versión mayor (más siginficativa) del controlador. getMinorVersion Obtiene la versión menor (menos significativa) del controlador. getPropertylnfoDetermina las propiedadesbaserequeridaspor usando el controlador actual. el usuarioparacrearunaconexión jdbcCompliant Regresa un valor booleano que indicasi el objeto Driver actual es compatible con JDBC. De los anteriores metodossdlo nos ocuparemos deacceptsURL y connect. acceptsURL() El metodo acceptsURL de la interfaz Driver tiene la misibn de averiguar si el objeto Driver actual puede hacer una conexi6n en el URL especificado. El metodo toma un parametro de tipo String que contiene el URL de la base de datos con la que se desea establecerla conexi6n. Si el objeto Driver puede realizar una conexibn exitosa con el URL especificado el metodo regresara un valor verdadero, de lo contrario regresara un valor falso. Si no se conoce sobre URLs vease la secci6n dedicada a URLs dentro de este apartado. connect() El metodo connect permitecrearunaconexidnfísicaalabasededatosdesignadapor un parametro de tipo String. El metodo toma dos parametros. El primer parametro especifica el URL de la base de datos en forma de un objeto String y puede ser cualquier base de datos que el driver actualsoporte. El segundoparametroes un objeto Properties que contiene la informaci6n necesaria para conectarse ala base de datos, normalmenteel nombre de usuarioy la contrasena, pero puede contener otros elementos. Conexión con la interfaz Driver El Listado 10 ejemplifica el uso de los metodos acceptsURL y connect de la interfaz Driver para lograr la conexibn con la base de datos. Primero se determina si el URL es adecuado para una conexi6n de base de datos, y de serlo, se establece la conexi6n. 96 Listado I O . Conexi6n a una fuente de datos con la interfaz Driver. import java.util.*; import java.sql.*; public class Conexion { public Conexion ( ) I try ( / / Cargamos el driverde puente jdbc-odbc String drivername= "sun.jdbc.odbc.JdbcOdbcDriver"; Driver driver= (Driver) Class. forName (drivername) .newInstance (1 ; / / Determinamos si el URL es válido String url = "jdbc:odbc:coneccion"; if (driver.acceptsURL (url) ) I / / El URL es válido, conectamos a la BD System. out .println("URL válido"); / / Introducimos usuarioy contraseña Properties p= new Propertieso; p.put ("user","kuno"); p.put ("password", "kodachi") ; / / Efectuamos la conexión a laBD Connection c= driver.connect (ur1,p) ; System.out.println("Conexión realizada ...") ; 1 else { / / El URL no es válido System.out .println ("URL NO válido"); 1 1 catch (SQLException e) { System. out .println("Error de JDBC . . . " + e .getMessage () ) ; 1 catch (Exception e) { System.out.println("Error . . . " + e.getMessage0 ) ; 1 1 public static voidmain (String args[]) Conexion app= new Conexion(); I { ) El driver de JDBC usado en el ejemplo es un puente JDBC-ODBC, por lo que el subprotocolo en el URL es odbc. El driver es cargado con el método forName de la clase genérica Class y al mismo tiempo es convertido en un objeto Driver e instanciado dentro de una variable para poder invocar sus métodos acceptsURL y connect. Si el URL es valido, se crea un objeto Properties y se pasa como parametrojunto con el URL al metodo connect para efectuarla conexibn. El objeto DriverManager El objeto DriverManager se usa como administrador de objetos Driver; para ello se sirve de la propiedad jdkdrivers del sistema en el objeto System. Con esta propiedad podemos especificar distintos controladores JDBC para diferentes aplicaciones.Se pueden accesar las propiedades del sistema mediante el método getProperties() del objeto System. Después de haber modificado el valorde la propiedad jdbc.drivers, se pueden asignar las propiedades del sistema utilizando el mktodo setProperties. EnlaTabla 26 se enlistan los mktodos del objetoDriverManager y sus descripciones: riDci6n 97 Tabla 26. M6todos del objetoDriverManager. Método deregisterDriver Elimina un objeto Driver de getConnection Crea una conexión la lista de controladores. a base la de datos. getDriver Localiza objeto un getDrivers Regresa un arreglo que contienetodos los objetos Driver actualmente registrados con el administrador. getLoginTimeoutRegresa el número de segundosqueesperará conexión. getLogStream Regresa el flujo de Registro/Rastreo (loggin/tracingstream) que usará administrador paralos objetos Driver. println Envía la cadena especificada al flujo actual de registro. registerDriver Registra con el adminstrador el objeto Driver especificado. setLoginTimeoutAsigna el númeromáximodesegundosqueesperará establecer la conexión. setLogStream Establece Driver que conectará al URL especificado. un controladorparaestableceruna el el controlador para el flujo de Registro/Rastreo que usarán los objetos Driver. El metodo que ahora nos interesa esel m6todo getconnection. getconnection El método getconnection del objeto DriverManager tiene tres variantes distintas en cuanto a los parámetros que acepta, pero todas ellas crean una conexi6n a la base de datos y regresan un objeto Connection. La primera forma de getconnection s610 toma el URL para la base de datos e intenta la conexi6n con esta usando el objeto Driver actual o un objeto Driver adecuado de la lista de objetos Driver registrados.En6stavarianteseasumequenoserequierenombre de usuario,contrasena ni ninguna otra propiedad de base de datos para establecerla conexi6n. La segunda forma de getconnection toma el URL para la base de datos, pero tambien toma un objeto Properties que contiene varias propiedades requeridas para conectarse a la base de datos. El segundo parametro debe ser un objeto Properties que contenga los elementos necesarios para hacer una conexidn a la base de datos especificada. La última variante de getconnection toma, como las dos anteriores, el URL de la base de datos, peroenlugar de tomarunobjeto Properties, setomandosobjetos String. Laprimeracadena el nombre de usuario identifica el nombre de usuario y la segunda contiene la contrasena para dado. Conexión conel objeto DriverManager El ejemplo mostrado en el Listado 11 ilustra la forma en que se hace la conexih a la base de datos coneccion usando el objeto DriverManager. Se utiliza la tercervariantedelm6todo getconnection usando las mismas propiedadesde usuario y contrasena del ejemplo anterior: 98 Listado 11. Conexión a una fuente de datos con el objeto DriverManager. import java.util.*; import java.sql.*; public class Conexion2 ( public Conexion2 ( ) { try I String drivername= "sun.jdbc.odbc.JdbcOdbcDriver"; / / Registra el driver jdbc-odbcen las propiedades del sistema Properties system = System.getProperties0; system.put ("jdbc.drivers",drivername); System.setProperties(system); / / Cadenas de URL, usuario y contraseña String url = "jdbc:odbc:coneccion"; String usr= "kuno"; String psw = "kodachi"; / / Realiza la conexión Connection c = DriverManager.getConnection(ur1, usr, psw); System.out.println("Conexi6n realizada . . . ") ; } catch (SQLException e) ( System. out .println("Error de JDBC . . . " + e.getMessage ( ) 1 catch (Exception e) { System. out. println ("Error . . . " + e.getMessage ( ) ) ; ) ; ) 1 public static void main (String args[]) ( Conexion2 app = new Conexion20; } } Igual que antes, se utiliza un puente JDBC-ODBC. Este controlador se registra en la propiedad jdkdrivers del sistema. Posteriormente se crean las cadenas del URL, el usuario y la contraseiia, lascualessepasancomoparametrosen la tercervariante del metodogetConnection.Sin embargo, bien pudo usarse la primer forma, la que s6lo requiere del URL, puesto que algunas de las bases de datos de Access no precisan de usuario y contraseiia para ser accesadas desde la máquina local. Este esel caso de la base de datoscunecciun. Sobre los URLs de JDBC URLson las siglas para Uniform Resource Locator (LocalizadorUniformedeRecursos) y proporciona la informaci6n para localizar recursos en la Internet. Ejemplos típicos de URLs son los usados para acceder a paginas desde un browser, por ejemplo http://java.sun.com, es un ejemplo de URL para el protocolo de hipertexto. La primera parte del URL especifica el protocolo que se usaparaaccesarlainformaci6n y siemprevaseguidodedospuntos ":". Algunosprotocolos comunes son ftp, http y file. El resto del URL, todo lo que sigue a los dos puntos, da la informaci6n de donde se localiza el recurso. Si el protocolo es file, el resto del URL es el path al archivo. Para los protocolos ftp y http, el resto delURLidentifica al host y opcionalmente el pathhaciauna página especifica. Por ejemploel URL: http://java.sun.com sólo especifica el host; y el URL: http://java.sun.com/products/jdbc identifica la pagina de JDBC de Sun. 99 Un URL de JDBC proporciona una forma de identificar una base de datos de forma que el driver adecuado la reconozcay establezca una conexidn con ella. La sintaxis para un URL de JDBC tiene tres partes separadas por":" y se muestra enseguida: jdbc:<subprotocolo>:<nombre> Las tres partes se describen por separado: 1. jdbc-el protocolo. El protocolo en un URL de JDBC siempre es jdbc. 2. <subprotocolor"el nombre del driver o el nombre del mecanismo de conexi6n a la base dedatos, el cual puedesersoportadoporuno o mas drivers.Unbuenejemplode subprotocolo es "odbc", el cual se reserva para URLs que especifican nombres de fuentes de datos de tipo ODBC. Por ejemplo, para accesar una base de datos a traves de un puente JDBC-ODBC, se podría usar el URL siguiente: jdbc:odbc:base En &te ejemplo, el subprotocolo es "odbc" y el nombre es "base", que es un nombre de fuente de datos de ODBC. 3. enombrer-una maneradeidentificarlabasededatos. El nombre puede variar dependiendo del subprotocolo. El objetivo del nombre es el de proporcionar la suficiente informaci6n para localizar la base de datos. En el ejemplo anterior, "base" es suficiente porque ODBC se encarga de dar el resto de la informaci6n requerida. Una base de datos en un servidor remoto requiere de mas informaci6n. Si la base de datos es accesada por Internet, por ejemplo, la direcci6n de red debe incluirse como parte del nombre y debe seguir la nomenclatura URL estandar: //nombrehost:puerto/subnombre El subprotocolo odbc es un caso especial. Se reserva para URLs con nombres de fuentes de datos detipoODBC y tienecomocaracterísticadistintiva el permitircualquiernúmerodeatributos despues del nombre (nombre de fuente de datos). La sintaxis completa para el subprotocolo odbc es la siguiente: Un ejemplo de un URL valido con subprotocolo odbc es: jdbc:odbc:base;UID=ryoga;PWD=p-chan 10.3 Creación y ejecución de sentencias deSQL Al hacer la conexión con la base de datos, ya sea usando el objeto Driver o usando el objeto DriverManager,secreaunobjetoConnection.ConelobjetoConnectionpodemosobtenerun objeto Statement para la ejecucibn de sentencias deSQL, y para obtener los resultados. Ambos, el objeto Connection y el objeto Statement, son interfaces de Java, con Bstas interfaces podemos crear y ejecutar instrucciones SQL y tambien obtener los resultados. Se vera un ejemplo de c6mo se puede lograr esto, pero antes estudiemos los elementos necesarios paratal prop6sito. 1O0 La interfaz Connection La interfaz Connection nos permitehacervariasoperacionescon la basededatos y obtener informaci6n de esta, ofrece varios metodos para el manejo de transacciones y de procedimientos almacenados. Proporciona tambien algunos metodos para el manejo de errores. Pero uno de los metodos mas útiles de la interfaz Connection es el que nos proporciona un objeto Statement, el metodo createStatement. Otro metodo importante de la interfaz Connection es el metodo close, con el cual se cierra la conexi6n con la basede datos. Los metodos que utilizaremosde la interfaz Connection son: close El método close cierra explícitamente la conexi6n a la base de datos. La conexi6n se cierra de todas formas al terminar la aplicacibn. Este metodo es útil cuando se tiene un número limitado de conexiones disponibles a la base de datos. Con el metodo close se puede terminar una conexibn despues de uncierto periodo de inactividad. createstatement El metodo createstatement crea un objeto Statement y lo regresa como valor. El objeto Statement se usa para ejecutar sentenciasde SQL y obtener resultados.Se utiliza para ejecutar instrucciones SQL estaticas. Para las instrucciones SQL dinarnicas o procedimientos almacenados se usan los objetos Preparedstatementy CallableStatement. La interfaz Statement Seha vistoque el objeto Connection nossirveparaconectarnosaunabase de datos.Para Statement. El ejecutarinstruccionesde SQL y obtener los resultadosdebemosusarunobjeto objeto Statement, de igual manera que el objeto Connection, no puede crearse en forma directa. Para crearun objeto Statement se asigna el valor de regreso de un metodo de otro objeto, el objeto Connection. El metodo createstatement del objeto Connection devuelve un objetoStatement como valor. Se puede usar un objeto Statement para ejecutar instrucciones SQL estaticas y obtener de vuelta resultados de consultas SQL. Una instrucci6n SQL estdtica no toma ningún argumento para estar completa. Una instruccidn dinarnica, en cambio, no esta completa hasta que se pasa cierto número select,delete, de argumentosa la instrucci6nSQL.Instrucciones SQL estaticaspuedenser update, insert e incluso un procedimiento almacenado. Las instruccionesupdate, delete e insert no regresan ningún resultado;estos procedimientos simplemente actualizan datos en la base de datos y no regresan nadade ella. Un procedimiento almacenado puede llevar a cabo cualquier operaci6n de actualizaci6n de datos y puede tambien regresar una selecci6n de resultados de la base de datos. Los métodosdel objeto Statement que nos interesanson: close El metodo close del objeto Statement libera de maneraexplícitalassentencias y recursos asociados al objeto Statement. Se liberan tanto los recursos usados porlos objetos del controlador JDBC como los recursos que esten en uso en el servidor de base de datos. Tambien se usa el método close para liberar cualquier bloqueo colocado a la base de datos por la ejecucidn de las instrucciones de SQL. 101 executeQuery El mdtodo executeQuery tiene como propdsito el envio de instrucciones select de SQL para la obtencidn de los resultados que proporcionen. Los resultados se reciben en un objetoResultset. El método executeQuery toma como parametro una instruccidn select de SQL y regresa un objeto Resultset que contiene los registros que coincidieron con los criterios dela instruccidn select. executeupdate El método executeupdate se utiliza para ejecutar instrucciones SQL de actualizacidn tales como delete, insert y update. El rnetodo toma como parametro una cadena que contiene la instruccidn SQLde actualizacidn y regresa un entero que indica cuantos registros fueron afectados por la instrucción SQL. Adicionalmente, se pueden ejecutar instrucciones DDL de SQL que no regresan nada. getResultSet El mdtodogetResultSetseutilizaparaobtener Statement y regresarlo como un objeto Resultset. el conjuntoderesultadosactualdelobjeto El objeto Resultset El objeto Resultset es parecido a un arreglo bidimensional. Se compone de un número finito de registros, o filas, y cada registro contiene un conjunto finitode columnas. Cada una de las casillas formadas por las filasy columnas contienen datos, los cuales pueden ser de tipo cardcter, enteroo nulo. LaFigura 11 muestraunarepresentacidnvisualdeunobjeto Resultset. Lailustracidn presentadatiene cinco registrosqueasu vezcontienencincocolumnascadauno.Encada los métodosdelobjeto columnahayunaporcidndedatosquepuedeseraccesadamediante Resultset. Columnas Figura 11. Representacion gráfica del objeto ResultSet. AI obtener el objeto Resultset devueltadelabasededatostodavianopodemosver los resultados. Para visualizar los resultados que fueron regresados, debemos usarlos metodos next y getXXX del objetoResultset. Estos metodos proveen la navegacidn a traves del objeto Resultset y el accesoavaloresdedatos en dichoconjuntoderesultados,respectivamente. Los metodos getXXX dan una forma de obtener los valores de datos en el tipo de datos deseado, por ejemplo, 102 para obtener valores Float podemos usar el metodo getFloat para regresar una columna como un objeto Float, para obtener una cadena se usa el metodo getstring para regresaruna columna como un objeto String y así sucesivamente. El metodo next mueve el cursor del objeto Resultset al siguiente registro en el conjunto de resultados, aunque inicialmente el apuntador comienza en un registro NULL que antecedeal primer registro real; por ello se debe llamar al metodo next antes de intentar accesar cualquiera de los datos contenidos en el conjunto de resultados. El metodo next regresa un valor booleano que indicasi existe un registro en la posici6n de cursor actual. Si hay un registrovididoen la posici6na la quesemueve el cursor, el metodoregresara true; encaso contrario, regresara false. Otro objeto interesante es la interfaz ResulfSetMefaData. Esta interfaz proporciona datos relevantes al conjunto de resultados, como por ejemplo,el numero de las columnas regresadasen el objeto Resultset. Mas adelante se vera un poco mas sobre 6ste objeto. Un ejemplo simple de ejecución de instrucciones de SQL Con lo aprendido anteriormente estamos listos para crear nuestra aplicaci6n Java para ejecuci6n de instrucciones SQL. Usaremos como antes la base de datos de Access cuyo DSN esconeccion. Construyamos nuestra aplicaci6n por pasos. Lo primero es, por supuesto, efectuar la conexi6n, para ello usaremos la interfaz DriverManager y cargaremos el driver JDBC-ODBC directamente con el metodo forName de la clase genericaClass: Class. forName ( "sun. jdbc. odbc. JdbcOdbcDriver") ; String url= " jdbc: odbc: coneccion"; String uid= "tendo"; String psw = "nabiki"; Connection c = DriverManager.getConnectíon(ur1, uid, psw); Como ya tenemos el objeto Connection, podemos entonces obtener, a partir de este, un objeto Statement, con el metodo createStatemenf: Statement stmt= c.createStatemento; Con el objeto Statement recien creado, probaremos el crear una tabla t_prueba enlabasede datos coneccion. Posteriormente insertaremos registrosen la tabla para despues leerlos mediante una instruccih select de SQL. Estas operaciones son las mismas que se hicieron en el ejemplo de uso de las funcionesdel m6dulo de Visual Basic: Conexion.bas,del capítulo 8; las instrucciones de SQL son las mismas: //Se crea la tabla de ejemplo sql = "CREATE TABLE t-prueba ( " ; sql += "id INTEGER CONSTRAINT constl PRIMARY KEY, sql += "dato1 TEXT ( 2 0 ) , sql += "dato2 TEXT(30)"; sql += " ) ;"; stmt .executeupdate ( s q l ); 'I; 'I; //Se insertan dos registros: sql = "INSERT INTO t-prueba VALUES (1, \'pruebal\', \'texto pruebal\');" sql += "INSERT INTO t-prueba VALUES ( 2 , \'prueba2\', \'texto prueba2\');" int val = stmt.executeUpdate(sq1); //se realiza la consulta sql = "SELECT * FROM t-prueba WHERE id = 1 ; " ResultSet rs = stmt.executeQuery(sq1); 103 Nótese que se us6 el caracter de escape Y en las instrucciones insert de SQL. AI igual que Java, SQL requiere que las constantes de cadena estdn contenidas en alguna forma de entrecomillado. Se debe emplear el caracter de escape ( \ ) debido a que la mayoria de los controladores colocan un ap6strofe ( ' ) antes y despuds de cada instruccidn de SQL que envian a la base de datos.Si se omiteelcaracterdeescapeantesdelacomillasencilla,entonces el controladorJDBC lo interpretara comoel fin de un comando SQL a la base de datos. Tenemos pues, en dste momento, el objeto ResultSef, que nos permitirh visualizar la informacibn extraídaconlainstruccidn select. Usaremos el mdtodo getstring delobjeto Resultset que convenientemente nos dara los datos como objetosString para su facil presentacidn: //Obtenemos el número de columnas ResultSetMetaData rsmd= rs.getMetaData0; int cols = rsmd.getColumnCount(); //Se despliega la información String registro = ""; while (rs.next( ) ) { for (int i=l; i<=3; i++) registro += rs.getString(i) System.out.println(registro); + " "; A partir del objeto Resultset, creamos un objeto ResultSetMetaData con el mdtodo gefMetaData delobjeto Resultset. El mdtodo getColumnCount delobjeto ResultSetMetaData devuelveel número de columnas regresadas en el conjunto de resultados. Un objeto ResultSetMetaData nos brinda informacidn variada sobre un conjunto de resultados representado por un objeto ResulfSet. La parte final de nuestro programa es la limpieza y liberaci6n de recursos, o sea, cerrar los objetos ResultSet, Statement y Connection explícitamente en lugar de esperar que sean cerrados en forma o al terminar la aplicacidn: automatica por el colector de basura, rs.close ( ) ; stmt. close () ; c.close ( ) ; De dsta forma hemos finalizado nuestra pequefia aplicacidn Java que se conecta a una base de y consultas. Los objetos de Java aquí presentadosde la API de JDBC datos y hace actualizaciones son los mínimos para hacer operaciones de bases de datos. El listado de cddigo completode dsta aplicaci6n se muestra en el siguiente apartado. Tambidn se describen las herramientas de JDK usadas para la compilacidny prueba de los applets. 10.4 Las herramientas deJDK Hasta ahora, se han visto ejemplos de cddigo Java que ilustran algunas de las clases de la API de JDBC. Pero no se ha explicado como es que se sabe que funcionan. Todos los ejemplos de la partedeJDBCdedstetrabajosecodificaronusandocualquiereditordetexto, el NotePad de Windows fue uno delos mas usados. Para compilarlos y correrlos se usaron las herramientas javac y java del Java Development Kit (JDK 1.1.6) respectivamente. Existen, sin embargo, herramientas visuales para desarrollo con Java, por ejemplo el JBuilder y el Developer Studio, dstas permiten ciertodesarrollodetipoRAD(RapidApplicationDevelopment). El autor sdlo seaboc6alas herramientas de JDK que se describen en este apartado. 104 javac-El compilador de Java javac se utiliza para compilar programas Java. SINTAXIS javac [opciones] Archivo1.java Archivo2.java ... ArchivoN.java El comando javac compila cbdigo fuente de Java a cbdigo objeto. Después de eso se puede usar el intérprete deJava-el comando java-para interpretar el cbdigo objeto de Java. ElcbdigofuentedeJavadebeestarcontenido en archivoscuyosnombresfinalicenconla extensión .java. El nombre de archivo debe coincidir con el nombre de la clase, como nombre-clase. j ava, si la clase es publicao es referenciada desde otro archivo fuente. Para todas las clases definidasen cada archivo fuente compiladocon javac, el compilador guarda los archivosobjetoresultantesen un archivoclass,guardandolo con unnombredelaforma nombre-clase.class. A menos que se especifique la opción -d, el compilador coloca los archivos class en el mismo directoriodel archivo fuente. Cuando se requieren de clases propias es necesario especificar la localizacibn de éstas. Para ello seusa la opcibn -classpath o lavariabledeambiente CLASSPATH. El classpathes una secuencia de directorios (o archivos zip) en los que javac busca las clases no definidasen alguno de los archivos especificados directamente como argumentos del comando. El compilador busca en los directorios seAaladosel archivo objeto y el archivo fuente, recompilando el archivo fuente (y regenerando el archivo objeto)si M e es mas reciente. ALGUNAS OPCIONES -classpath rutas Especifica las rutas en las que javac buscara las clases necesarias o que son referenciadas por las clases que se estan compilando. Tiene preferencia sobre el directorio default o la variable de ambiente CLASSPATH. Las rutas se separan con punto y coma. Siempre es Útil que el directorio que contiene los archivosfuenteeste en el classpath. Es buenoincluirsiemprelasclasesdel sistema al final de las rutas.Por ejemplo: ... javac -classpath .;C:\users\dac\classes;C:\tools\java\classes -d directorio Especifica el directorioraíz de la jerarquia dearchivosdeclases.Enotraspalabras,esto esencialmente un directoriode destino para las clases compiladas. Por ejemplo, el comando: es javac -d C:\users\dac\classes MiPrograma.java causa que los archivos de clases para las clases contenidas en el programa MiPrograma.java se guarden en el directorio C:\users\dac\classes. Si las clases estanen el paquete demosbwt, las clases seren colocadas enel directorio C:\users\dac\classes\demosbwt. Note que las opciones -d y elasspath tiene efectos independientes.El compilador s610 lee desde el classpath, y sblo escribe en el directorio de destino. Siempre es útil que el directorio de destino 105 este en el classpath. Si no se especifica la opcidn -d, los archivos de clases son guardados en el directorio actual como directorio raíz. deprecation Genera advertencias por el uso de clases o metodosdiscontinuados.Unmetodo o claseestá discontinuado si en su documentacidn contiene la marca @deprecated. El compilador emitir6 una advertencia al final de la compilacidn aún si la opcidn deprecation no es usada;estaopci6n causa que se seflalenlos metodos o clases discontinuadas que se usaron. -verbose Causa que el compilador y el enlazador impriman mensajes sobre que archivos fuente se est6n compilando y que archivos de clases se est6n cargando. java-El Intérprete de Java java ejecuta una aplicacidn Java. SINTAXIS java [opciones] nombreclase <args> El comando java ejecuta una aplicacidn Java. Esto mediante el inicio de un JRE (Java Runtime Environment), la carga de la clase especificada, y la invocacidn del mbtodo main de la clase. El comando java ejecuta archivos Java.class creados por el compilador de Java, javac. El argumento nombreclase es el nombre de la clase ha ser ejecutada. paquete de procedenciaen el nombre, por ejemplo: nombreclase debe incluir su java java.lang.String Cualesquieraargumentosqueseincluyanenlalíneadecomandodespu6sde nombreclase se pasarán al metodo main de la clase. La clase a ejecutar debe contener un m6todo main definido como sigue: class Aclass { public static . . . I void main(String argv[l) { 1 java ejecuta el metodo main y luego termina el programa a menos queel metodo main cree uno o más hilos de ejecución. Si se crearon hilos de ejecucidnel comando java no termina hasta que el último hilo termina. Cuando se definen clases propias se necesita especificar su localizacidn. Se utiliza la variable de ambiente CLASSPATH para ello. CLASSPATH consiste de una lista de directorios separados por puntos y comas que especificanlas rutas. Por ejemplo: .;C:\xyz\classes 106 ALGUNAS OPCIONES -classpath rufas Especifica las rutas en las que el comando java buscara las clases. Tiene preferencia sobre la variable de ambiente CLASSPATH si esta es utilizada. Los directorios van separados por punto y coma. Por ejemplo: C:\xyz\classes;C:\usr\local\java\classes -noasyncgc Desactiva la recolecci6n de basura asincrona. Cuando se desactiva la recoleccibn de basura esta no se lleva a cabo hasta que es explícitamente invocada o el programa sufre un desbordamiento de memoria. Normalmente la recolecci6n de basura corre como un hilo de ejecucidn asíncrono en paralelo con otroshilos. -nojit No invocar el compilador de c6digo objeto a c6digo maquina, Just In Time. La maquina virtual interpreta directamenteel c6digo objeto, sin convertirlo a c6digo maquina. -version Imprime informaci6n de laversih. -help Imprime un mensaje sobrela utilizaci6n. -v, -verbose Ocasiona que se imprima un mensaje sfdout a cada vez que un archivo de clase es cargado. -verbosegc Ocasiona que el recolector de basura imprima mensajes cuando este libera memoria. Compilando y corriendo un ejemplo El ejemplo que se usara en esta ocasi6n es el visto en el apartado anterior. En este ejemplo se unen todos los elementos de JDBC revisados, pues se realiza una conexi6n de base de datos, se actualiza una tabla y despues se hace una consulta y se presentan los resultados. Presentamos pues, el listadocompletodelejemplodeejecuci6ndeinstruccionesde SQL. Losmensajesde salida a lo largo del programa son para llevar un seguimiento del mismo: Listado 12. Ejecución de instrucciones de SQL. import java.sql.*; import java.util.*; public class EjecucionSQL { public EjecucionSQLO { try { / / S e carga el driver JDBC-ODBC 107 Class.forName("sun.jdbc.odbc.JdbcOdbcDriverlf); System.out.println("Driver JDBC-ODBC cargado //Se preparan las cadenas de URL, usuario y contraseña String url = "jdbc:odbc:coneccion"; String uid = "tendo"; String psw = "nabiki"; //Se realiza la conexión y se crea un objeto de sentencia Connection c = DriverManager.getConnection(ur1, uid, psw); System.out.println("Conexion realizada ; Statement stmt= c.createStatemento; //Se crea la tabla de ejemplo String sql = "CREATE TABLE t-prueba ( " ; sql += "id INTEGER CONSTRAINT constl PRIMARY KEY, "; sql += "dato1 TEXT(20) sql += "dato2 TEXT(30) "; sql += " ) ;"; stmt .executeupdate (sql); System.out.println("Tab1a de ejemplo creada .. ; //Se insertan dos registros: sql = "INSERT INTOt-prueba VALUES (1,\ 'pruebal\ I , \ texto pruebal\) int val = stmt.executeUpdate(sq1); sql = "INSERT INTO t-prueba VALUES (2,\ 'prueba2\ ,\ texto prueba2\) val = stmt.executeUpdate(sq1); Systern.out.println("Registros agregados ala tabla . . . ") ; //se realliza la consulta sql = "SELECT * FROM t-prueba;"; Resultset rs = stmt.executeQuery(sq1); System.out.println("Consu1tahecha. Resultados:"); //Obtenemos el número de columnas ResultSetMetaData rsmd= rs.getMetaData(); int cols = rsmd.getColumnCount(); //Se despliega la información while (rs.next ( ) ) { String registro = " " ; for (int i=l; i<=cols; i++) registro += rs.getString(i) + " "; System.out.println(registro); I //Cerramos objetos y conexión rs.close ( ) ; stmt.close ( ) ; c. close () ; System.out.println ("Conexion cerrada , .. ; . . . ' I ) ; . . . ' I ) , ' I ; .'I) ;"; ;'I; 'I) I catch (SQLException e) { System.out.println("Error de SQL: " + e.getMessage()); } catch (Exception e) ( System.out.println ("Error: " + e.getMessage ( ) ) ; I public static voidmain (String argv[]) { EjecucionSQL app= new EjecucionSQLO; Para compilareste programa y generar la clase de Java usaremosel comando javac. Suponiendo que tenemos el JDK instalado en el directorio C:\jdkl.l.G, y que nuestro archivo fuente estA en el directorio C:\jdkl .I .6\applets, simplemente haremos: C:\>cd \jdkl.l.6\applets C:\jdkl.l.G\applets>c:\jdkl.l.6\bin\javac EjecucionSQL.java 108 si en la variable de ambientePATH se agrega la ruta C:\jdkl. 1.6\bin, solamente bastarla escribir: C:\jdkl.l.G\applets>javac EjecucionSQL.java si no existeningúnerror,despues de lacompilacióndelarchivofuentesehabragenerado el archivo objeto: EjecucionSQLdass. Para correr este programa usaremos el comando java como sigue: C:\jdkl.l.G\applets>java EjecucionSQL es importante respetar las letras mayúsculas y minúsculas del nombre de la clase, pues como en C, las variables y nombres de objetos se diferencian en el uso de mayúsculas y minúsculas; los nombres EjecucionSQL y ejecucionsql representan objetos distintos para Java. Veamos ahora lo que ocurre despues de ejecutar el programa: C:\jdkl.l.G\applets>java EjecucionSQL Driver JDBC-ODBC cargado. . . Conexion realizada . . . Tabla de ejemplo creada . . . Registros agregados ala tabla . . . Consulta hecha. Resultados: 1 pruebal texto pruebal 2 prueba2 texto prueba2 Conexion cerrada . . . C:\jdkl.l.G\applets> Vemos que el programa corrió con dxito, pues nose generaron excepcionesque fueran mostradas entre los mensajes que se colocaron a propdsito. De la salida del programa observamos tambien los dos registros regresados como resultado de la consulta a la tabla Lprueba. Esta tabla debera o bienpuedeserunbuen serborradaposteriormentede la basededatosenformamanual, ejercicio parael lector agregar el código necesario al programa EjecucionSQL para eliminar la tabla antes de cerrar la conexión. 1o9 Para más información Para una mayorreferenciasobrelasclasesdelaAPIde JDBC y susmktodos,consúltese documentacidn deJDK, o bien se puede ir directamente al sitio de JavaSoft: la http://iava.sun.com/products/idkll.2/docs/quide/idbc/index.html A 6stas alturas ya debe haber sido liberada la versidn 2.0 de JDBC junto con el JDK 1.2, la cual y mejorassobrelasexistentes.SunMicrosystems seguramentepresentaráclasesnuevas constantemente libera versiones mejoradas deJDK y JDBC. Para mantenerse al corriente con las mismas se recomienda consultar con regularidad el sitio de JavaSoft: http://iava.sun.com PARTE IV. Apéndices O Declaración de las funciones dela API de ODBC O Conexion.bas: Código de las funciones A Declaración de las funciones de la API de ODBC. Se muestra a continuacibn el contenido del archivo ODBC32.bas. En 61 se hace la importacibn de las funciones de la libreria odbc32.dll para su uso en Visual Basic. Este archivo contiene tambikn las constantes y tipos de datos usados porla mayoría de las funciones de la API de ODBC. Listado 13. Listado del módulo ODBC32.bas. 'Attribute VB-Name #If Win32Then = "ODBC32" 1 I .......................................................................... I I ODBC módulodelnúcleo Definiciones del """"~"""~""""""""""""""""""""""""1"""" ) """"""""""""""""""""""""-""""""""~ 1 Definiciones API del nucleo ODBC-- versiones de 32 bits I Declare Function SQLAllocConnect Lib "odbc32.dll" (ByVal henv&, phdbc&) As Integer Declare Function SQLAllocEnv Lib "odbc32.dll" (phenv&) As Integer Declare Function SQLAllocStmt Lib "odbc32.dll" (ByVal hdbc&, phstmt&) As Integer Declare Function SQLBindColLib "odbc32.dll" (ByVal hstmt&, ByValicol%, ByVal fCType%, rgbValue AsAny, ByVal cbValueMax&, pcbValue&) As Integer Declare Function SQLCancel Lib "odbc32.dll" (ByVal hstmt&) As Integer Declare Function SQLColAttributes Lib "odbc32.dll" (ByVal hstmt&, ByVal icol%, ByVal fDescType%, rgbDesc As Any, ByVal cbDescMax%, pcbDesc%, pfDesc&) As Integer Declare Function SQLColAttributesString Lib "odbc32.dll" Alias "SQLColAttributes" (ByVal hstmt&, ByValicol%, ByVal fDescType%, ByVal rgbDesc AsString, ByVal cbDescMax%, pcbDesc%, pfDesc&) As Integer Declare Function SQLConnect Lib "odbc32.dll" (ByVal hdbc&, ByVal szDSN$, ByVal cbDSN%, ByVal szUID$, ByVal cbUID%, ByVal szAuthStr$, ByVal cbAuthStr%) As Integer Declare Function SQLDescribeCol Lib"odbc32.dll" (ByVal hstmt&, ByValicol%, ByVal szColName$, ByVal cbColNameMax%, pcbColName%,pfSqlType%, pcbColDef&, - 113 pibScale8, pfNullable%) As Integer Declare Function SQLDisconnect Lib "odbc32.dll" (ByVal hdbc&) As Integer Declare Function SQLError Lib "odbc32.dll" (ByVal henv&, ByVal hdbc&, ByVal hstmt&, ByVal szSqlState$, pfNativeError&, ByVal szErrorMsg$, ByVal cbErrorMsgMax%, pcbErrorMsg%) As Integer Declare Function SQLExecDirect Lib "odbc32.dll" (ByVal hstmt&, ByVal szSqlStr$, ByVal cbSqlStr&) As Integer Declare Function SQLExecute Lib "odbc32 .dll" (ByVal hstmt&) As Integer Declare Function SQLFetch Lib "odbc32.dll" (ByVal hstmt&) As Integer Declare Function SQLFreeConnect Lib "odbc32.dll" (ByVal hdbc&) As Integer Declare Function SQLFreeEnv Lib "odbc32.dll" (ByVal henv&) As Integer Declare Function SQLFreeStmt Lib "odbc32.dll" (ByVal hstmt&, ByVal foption%) As Integer Declare Function SQLGetCursorName Lib "odbc32.dll" (ByVal hstmt&, ByVal szCursor$, ByVal cbCursorMax%, pcbCursor%) As Integer Declare Function SQLNumResultCols Lib "odbc32.dll" (ByVal hstmt&, pccol%) As Integer Declare Function SQLPrepareLib "odbc32.dll" (ByVal hstmt&, ByVal szSqlStr$, ByVal cbSqlStr&) As Integer Declare Function SQLRowCount Lib"odbc32.dll" (ByVal hstmt&, pcrow&) As Integer Declare Function SQLSetCursorName Lib "odbc32.dll" (ByVal hstmt&, ByVal szCursor$, ByVal cbCursor%) As Integer Declare Function SQLSetParam Lib "odbc32.dll" (ByVal hstmt&, ByVal ipar%, ByVal fCType%, ByVal fSqlType%, ByVal cbColDef&, ByVal ibScale%, rgbValue As Any,pcbValue.5) As Integer Declare Function SQLTransact Lib "odbc32.dll" (ByVal henv&, ByVal hdbc&, ByVal fType%) As Integer I 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . extendidas ' 1 Definiciones API de ODBC I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 , Prototipos de Nivel 1 Declare Function SQLBindParameter Lib "odbc32.dll" (ByVal hstmt&, ByVal ipar%, ByVal fParamType5, ByValfCType%, ByVal fSqlType%, ByVal cbColDef&, ByVal ibScale%, rgbValue As Any, ByVal cbValueMax&, pcbValue Long? As As Integer Declare Function SQLColumns Lib "odbc32.dll" (ByVal hstmt&, szTblQualifier As Any, ByVal cbTblQualifier%, szTblOwner As Any, ByVal cbTblOwner%, szTblName As Any, ByVal cbTblName%, szColName As Any, ByVal cbColName%) As Integer Declare Function SQLDriverConnect Lib "odbc32.dll" (ByVal hdbc&, ByVal hWnd As Long, ByVal szCSIn$, ByValcbCSIn%, ByVal szCSOut$, ByVal cbCSMax%, cbCSOut%, ByVal fDrvrComp%) As Integer Declare Function SQLGetConnectOption Lib "odbc32.dll" (ByVal hdbc&, ByVal foption%, ByRef pvParam As Any) As Integer Declare FunctionSQLGetConnectOptionString Lib "odbc32.dll" Alias"SQLGetConnectOption" (ByVal hdbc&, ByVal foption%, ByVal pvParam As String) As Integer Declare Function SQLGetData Lib "odbc32.dll" (ByVal hstmt&, ByVal icol%, ByVal fCType%, ByVal rgbValue As String, ByVal cbValueMax&, pcbValue&) As Integer Declare Function SQLGetNumericData Lib "odbc32 .dll" Alias "SQLGetData" (ByVal hstmt&, ByValicol%, ByVal fCType%, ByRef rgbValue As Any, ByVal cbValueMax&, pcbValue&) As Integer Declare Function SQLGetFunctions Lib "odbc32.dll" (ByVal hdbc&, ByVal fFunction%, pfExists%) As Integer Declare Function SQLGetInfo Lib "odbc32.dll" (ByVal hdbc&, ByVal fInfoType%, ByRef rgbInfoValue AsAny, ByVal cbInfoMax%, cbInfoOut%) As Integer dll" Alias "SQLGetInfo"Declare Function SQLGetInfoString Lib "odbc32. (ByVal hdbc&, ByValfInfoType%, ByVal rgbInfoValue As String, ByVal cbInfoMax%, cbInfoOut%) As Integer 115 Declare Function SQLGetStmtOption Lib "odbc32.dll" (ByVal hstmt&, ByVal foption%, ByRef pvParam As Any) As Integer Declare Function SQLGetStmtOptionString Lib "odbc32.dll" Alias "SQLGetStmtOption" (ByVal hstmt&, ByVal foption%, ByVal pvParam As String)As Integer Declare Function SQLGetTypeInfo Lib "odbc32.dll" (ByVal hstmt&, ByVal fSqlType%) As Integer Declare Function SQLParamData Lib "odbc32.dll" (ByVal hstmt&, prgbValue As Any) As Integer Declare Function SQLPutDataLib "odbc32.dll" (ByVal hstmt&, rgbValue As A n y , ByVal cbValue&) As Integer Declare Function SQLSetConnectOption Lib "odbc32.dll" (ByVal hdbc&, ByVal foption%, ByVal vParam As Any) As Integer Declare FunctionSQLSetConnectStringOption Lib "odbc32.dll" Alias "SQLSetConnectOption" (ByVal hdbc&, ByVal foption%, vParam$) As Integer Declare Function SQLSetStmtOption Lib "odbc32.dll" (ByVal hstmt&, ByVal foption%, ByVal vPararn&) As Integer Declare Function SQLSpecialColumns Lib "odbc32.dll" (ByVal hstmt&, ByVal fColType%, szTblQualifier AsAny, ByVal cbTblQualifier%, szTblOwner As Any, ByVal cbTblOwner%, szTblName As Any, ByVal cbTblName%, ByVal fScope%, ByVal fNullable%) As Integer Declare Function SQLStatistics Lib "odbc32.dll" (ByVal hstmt&, As Any, szTblQualifier As Any, ByVal cbTblQualifier8, szTblOwner ByVal cbTblOwner%, szTblName As Any, ByVal cbTblName%, ByVal fUnique%, ByVal fAccuracy%) As Integer Declare Function SQLTables Lib "odbc32.dll" (ByVal hstmt&, szTblQualifier AsAny, ByVal cbTblQualifier%,szTbl0wnerAs A n y , ByVal cbTblOwner%, szTblName As Any, ByVal cbTblName%, szTblType As Any, ByVal cbTblType%) As Integer ' Prototipos de Nivel Level 2 I Declare Function SQLBrowseConnect Lib "odbc32.dll" (ByVal hdbc&, ByVal szConnStrInS, ByVal cbConnStrIn%, ByVal szConnStrOutS, ByVal cbConnStrOutMax%, pcbConnStrOut%) As Integer Declare Function SQLColumnPrivileges Lib "odbc32.dll" (ByVal hstmt&, szTQf As Any, ByVal cbTQf%, szTOwn As Any, ByVal cbTOwn%, szTName As Any, ByVal cbTName%, szColName As Any, ByVal cbColName%) As Integer Declare Function SQLDrivers Lib"odbc32.dll" (ByVal henv&, ByValfDirection%, ByVal szDriverDesc$, ByVal cbDriverDescMaxB, pcbDriverDesc%, ByVal szDriverAttr$, ByVal cbDrvrAttrMax%, pcbDrvrAttr%) As Integer Declare Function SQLDataSources Lib "odbc32.dll" (ByVal henv&,ByVal fDirectionR, ByVal szDSN$, ByVal cbDSNMax%, pcbDSN%, ByVal szDescriptionS, ByVal cbDescriptionMax%, pcbDescription8) As Integer Declare Function SQLDescribeParam Lib "odbc32.dll" (ByVal hstmt&, ByVal ipar%, pfSqlType%, pcbColDef&, pibScale%, pfNullable%) As Integer Declare Function SQLExtendedFetch Lib "odbc32.dll" (ByVal hstmt&,ByVal fFetchType8, ByVal irow&, pcrow&, rgfRowStatus%) As Integer Declare Function SQLForeignKeys Lib "odbc32.dll" (ByVal hstmt&, ByVal PTQual&, ByVal PTQual%, ByVal PTOwnr&, ByValPTOwnr%, ByVal PTName&, ByVal PTName%,ByVal FTQual&, ByVal FTQf%, ByVal FTOwnr&, ByVal FTOwnr%, ByVal FTName&, ByVal FTName%) As Integer Declare Function SQLMoreResults Lib "odbc32.dll" (ByVal hstmt&) As Integer Declare Function SQLNativeSqlLib "odbc32.dll" (ByVal hdbc&, ByValszSqlStrJn$, ByVal cbSqlStrIn&, ByVal szSqlStrS, ByVal cbSqlStrMax&, pcbSqlStr&) As Integer Declare Function SQLNumParams Lib"odbc32.dll" (ByVal hstmt&,pcpar%) As Integer Declare Function SQLParamOptions Lib "odbc32.dll" (ByVal hstmt&, ByVal crow%, pirow&) As Integer Declare Function SQLPrimaryKeys Lib "odbc32.dll" (ByVal hstmt&, szTblQualifier As Any, ByVal cbTblQualifier%, szTblOwner As Any,ByVal cbTblOwner%, szTblName As Any, ByValcbTblName%) As Integer Declare Function SQLProcedureColumns Lib "odbc32 .dll" (ByVal hstmt&, - 116 szProcQualifier As Any, ByVal cbProcQualifier%, szProcOwner As Any, As Any, ByVal cbProcOwner%, szProcName As Any, ByVal cbProcName%, szColName ByVal cbColName%) As Integer Declare Function SQLProcedures Lib "odbc32.dll" (ByVal hstmt&, szProcQualifier As Any, ByVal cbProcQualifier%, szProcOwner As Any, ByVal cbProcOwner%, szProcName As Any, ByVal cbProcName%) As Integer Declare Function SQLSetPosLib "odbc32.dll" (ByVal hstmt&, ByVal irow%, ByVal foption%, ByVal flock%) As Integer Declare Function SQLSetScrollOptions Lib "odbc32.dll" (ByVal hstmt&, ByVal fConcurrency%, ByVal crowKeyset&, ByVal crowRowset%) As Integer Declare Function SQLTablePrivileges Lib "odbc32.dll" (ByVal hstmt&, szTblQualifier As Any, ByVal cbTblQualifier%, szTblOwner As Any, ByVal cbTblOwner%, szTblName As Any, ByVal cbTblName%) As Integer I ' Declaraciones de 32 bits Constantes y tipos de ODBC 1 ' Constantes de uso general I Global Const SQL-NTS As Long = -3 'NTS = Null Terminated String Global Const SQL-SQLSTATE-SIZE As Long = 5 'size of SQLSTATE Global Const SQL-MAX-MESSAGE-LENGTH As Long = 512 'message buffer size 'maximum data source name size Global Const SQL-MAX-DSN-LENGTH As Long = 32 ' RETCODEs 1 Global Global Global Global Global ' Const Const Const Const Const SQL-ERROR As Long = -1 SQL-INVALID-HANDLE As Long = -2 SQL-NO-DATA-FOUND As Long = 100 SQL-SUCCESS As Long = O SQL-SUCCESS-WITH-INFO As Long = 1 definiciones SQLFreeStmt 1 Global Global Global Global ' Const SQL-CLOSE As Long = O Const SQL-DROP As Long = 1 Const SQL-UNBIND As Long = 2 Const SQL-RESET-PAWS As Long = 3 definiciones SQLSetParam I Global Const SQL-C-DEFAULT As Long = 9 9 ' definiciones SQLTransact I Global Const SQL-COMMIT As Long = O Global Const SQL-ROLLBACK As Long = 1 ' Tipos de datos estándar SQL, usando los tipos ANSI 1 Global Global Global Global Global Global Global Global Global Const SQL-CHAR As Long = 1 Const SQL-NUMERIC As Long = 2 Const SQL-DECIMAL As Long = 3 Const SQL-INTEGER As Long = 4 Const SQL-SMALLINT As Long = 5 Const SQL-FLOAT As Long = 6 ConstSQL-REAL As Long = 7 Const SQL-DOUBLE As Long = 8 ConstSQL-VARCHAR As Long = 12 Global Const SQL-TYPE-MIN As Long = 1 Global Const SQL-TYPE-NULL As Long = 0 117 Global Const SQL-TYPE-MAX As Long = 12 ' Tipos de datos de C asignados a tiposde datos SQL I Global Global Global Global Global ' ' I Const SQL-C-CHAR As Long = SQL-CHAR Const SQL-C-LONG As Long = SQL-INTEGER Const SQL-C-SHORT As Long = SQL-SMALLINT Const SQL-C-FLOAT As Long = SQL-REAL Const SQL-C-DOUBLE As Long = SQL-DOUBLE 'CHAR,VARCHAR,DECIMAL,NUMERIC 'INTEGER 'SMALLINT ' REAL 'FLOAT,DOUBLE Constantes de estado de NULL. Se usan en SQLColumns, SQLColAttributes, SQLDescribeCol y SQLSpecialColumns para describirla capacidad de nulos de una columna en una tabla. SQL-NULLABLE-UNKNOWN sólo se puede devolver por SQLDescribeCol o SQLColAttributes. Se usa cuando el meta-datos de DBMS no contiene esta información. I Global Const SQL-NO-NULLS As Long = O Global Const SQL-NULLABLE As Long= 1 Global Const SQL-NULLABLE-UNKNOWN As Long = 2 ' Valores especiales de longitud 1 Global Const SQL-NULL-DATA As Long = -1 Global Const SQL-DATA-AT-EXEC As Long = -2 ' Definiciones de SQLColAttributes 1 Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Const SQL-COLUMN-COUNT As Long = O Const SQLCOLUMN-NAME As Long = 1 Const SQLICOLUMN-TYPE As Long = 2 Const SQL-COLUMN-LENGTH As Long = 3 Const SQL-COLUMN-PRECISION As Long = 4 Const SQL-COLUMN-SCALE As Long = 5 Const SQL-COLUMN DISPLAY-SIZE As Long= 6 Const SQL-COLUMNINULLABLE As Long = 7 Const SQL-COLUMN-UNSIGNED As Long = 8 Const SQL-COLUMN-MONEY As Long = 9 Const SQL-COLUMN-UPDATABLE As Long = 10 Const SQL-COLUMN-AUTO-INCREMENT As Long = 11 Const SQL-COLUMN-CASE-SENSITIVE As Long = 12 Const SQL-COLUMN-SEARCHABLE As Long = 13 Const SQL-COLUMN-TYPE-NAME As Long = 14 Const SQL-COLUMN TABLE-NAME As Long = 15 Const SQL COLUMN-OWNER-NAME As Long = 16 Const SQL~COLUMN~QUALIFIER-NAMEAs Long = 17 Const SQL-COLUMN-LABEL As Long = 18 Const SQL-COLATT-OPT-MAX As Long= SQL-COLUMN-LABEL Valores auxiliares de SQLColAttributes paraSQL-COLUMN-UPDATABLE I Global Const SQL-ATTR-READONLY As Long = O Global Const SQL-ATTRWRITE As Long = 1 Global ConstSQL-ATTR-READWRITE -UNKNOWN As ' ' Long = 2 Valores auxiliares de SQLColAttributes para SQL-COLUMN-SEARCHABLE También se usa en SQLGetInfo 1 Global Global Global Global Const SQL-UNSEARCHABLE As Long= O Const SQL-LIKE-ONLY As Long = 1 Const SQL-ALL-EXCEPT-LIKE As Long = 2 Const SQL-SEARCHABLE As Long= 3 118 Definiciones de SQLError I Global Const SQL-NULL-HENV As Long = O Global Const SQL-NULL-HDBC As Long = O Global Const SQL-NULL-HSTMT As Long = O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Definiciones extendidas globales de ODBC I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Definiciones y funciones de Nivel 1 ' Constantes de interés general I1 I 1 Global Const SQL-MAX-OPTION-STRING-LENGTH = 256 ' Códigos de retorno adicionales 1 Global Const SQL-STILL-EXECUTING As Long= 2 Global Const SQL-NEED-DATA As Long= 99 ' Tipos de datos extendidosde SQL 1 Global Global Global Global Global Global Global Global Global Global Global = 9 Const SQL-DATE As Long Const SQL-TIME As Long = 10 Const SQL-TIMESTAMP As Long = 11 Const SQL-LONGVARCHAR As Long = -1 Const SQL-BINARY As Long = -2 Const SQL-VARBINARY As Long = -3 Const SQL-LONGVARBINARY As Long = -4 Const SQL-BIGINT As Long = -5 Const SQL-TINYINT As Long = -6 Const SQL-BIT As Long = -1 Const SQL-TYPE-DRIVER-START As Long = -80 Tipos de datos de C asignados a tipos de datos de SQL I Global Const SQL-SIGNED-OFFSET As Long = -20 Global Const SQL-UNSIGNED-OFFSET As Long = -22 Global Global Global Global Global Global Global Global Global Global Global Global Global Const SQL-C-DATE As Long = SQL-DATE Const SQL-C-TIME As Long = SQL-TIME Const SQL-C-TIMESTAMP As Long = SQL-TIMESTAMP Const SQL-C-BINARY As Long = SQL-BINARY Const SQL-C-BITAs Long = SQL-BIT Const SQL-C-TINYINT As Long= SQL-TINYINT Const SQL-C-SLONG As Long = SQL-C-LONG + SQL-SIGNED-OFFSET Const SQL-C-SSHORT As Long = SQL-C-SHORT + SQL-SIGNED-OFFSET Const SQL-C-STINYINTAs Long = SQL-TINYINT + SQL-SIGNED-OFFSET Const SQL-C-ULONG As Long = SQL-C-LONG + SQL-UNSIGNED-OFFSET Const SQL-C-USHORT As Long = SQL-C-SHORT + SQL-UNSIGNED-OFFSET Const SQL-C-UTINYINT As Long = SQL-TINYINT + SQL-UNSIGNED-OFFSET Const SQL-C-BOOKMARK As Long= SQL-C-ULONG Global Const SQL-ALL-TYPESAs Long = O I ' Estructuras para marcas de fecha y hora 1 Type DATE-STRUCT As Integer year As Integer month 119 Integer Asday End Type Type TIME-STRUCT hour As Integer minute As Integer second As Integer End Type Type TIMESTAMP STRUCT year As- Integer Integer As month Integer As day Integer As hour Integer minute As second As Integer Long fractionAs End Type ' Opciones de SQLDriverConnect 1 Global Const SQL-DRIVER-NOPROMPT As Long = O Global Const SQL-DRIVER-COMPLETE As Long = 1 Global Const SQL-DRIVER-PROMPT As Long = 2 Global ConstSQL-DRIVER-COMPLETE -REQUIRED As Long = 3 ' Valores especiales de retorno para SQLGetData 1 Global Const SQL-NO-TOTAL As Long = -4 Extensiones de SQLSetParam 1 Global Const SQL-DEFAULT-PARAM As Long = -5 Global Const SQL-IGNORE As Long= -6 Global Const SQL-LEN-DATA-AT-EXEC-OFFSET As Long = -100 ' Definiciones para SQLGetFunctions ' Funciones del nucleo 1 Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Const SQL-API-SQLALLOCCONNECT As Long = 1 Const SQL API-SQLALLOCENV As Long= 2 Const SQLAPI SQLALLOCSTMT AS Long = 3 Const SQLAPI~SQLBINDCOLAS Long = 4 Const SQLAPI SQLCANCEL AS Long = 5 Const SQLAPI~SQLCOLATTRIBUTESAs Long = 6 Const SQL-API-SQLCONNECT AS Long = 7 Const SQLAPI-SQLDESCRIBECOL As Long = 8 Const SQL API SQLDISCONNECT As Long = 9 Const SQLAPI~SQLERRORAS Long = 10 Const SQL-API-SQLEXECDIRECT As Long = 11 Const SQL API SQLEXECUTE As Long= 12 Const SQLIAPIISQLFETCH As Long = 13 Const SQL API-SQLFREECONNECT As Long= 14 Const SQLAPI SQLFREEENV As Long= 15 Const SQLAPI-SQLFREESTMT As Long = 16 Const SQLIAPI~SQLGETCURSORNAMEAs Long = 17 Const SQL API-SQLNUMRESULTCOLS As Long= 18 Const SQLAPI-SQLPREPARE AS Long = 19 Const SQL-API-SQLROWCOUNT As Long = 20 Const SQL API SQLSETCURSORNAME As Long = 21 Const SQL~API~SQLSETPARAM AS Long = 22 Global Const SQL-API-SQLTRANSACT As Long= 23 Global Const SQL-NUM-FUNCTIONS As Long = 23 Global Const SQL-EXT-API-START As Long = 40 ' Funciones de nivel 1 1 Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Const SQL API SQLCOLUMNS As Long= 40 Const SQLAPIISQLDRIVERCONNECT As Long = 41 Const SQL~API-SQLGETCONNECTOPTLONAS Long = 42 Const SQL-API-SQLGETDATA As Long = 43 = 44 Const SQL API SQLGETFUNCTIONS As Long Const SQLAPI-SQLGETINFO AS Long = 45 AS Long = 46 Const Const SQLAPI SQLGETTYPEINFO AS Long = 47 = 48 Const SQLIAPIISQLPARAMDATA As Long Const SQL API-SQLPUTDATA As Long= 49 Const SQL~API-SQLSETCONNECTOPTIONAS Long = 50 Const SQL-API-SQLSETSTMTOPTION As Long = 51 Const SQL-API-SQLSPECIALCOLUMNS As Long = 52 Const SQL-API-SQLSTATISTICS As Long = 53 Const SQLAPI-SQLTABLES As Long = 54 SQLAPIISQLGETSTMTOPTION ' Funciones de nivel2 1 Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Const SQL-API-SQLBROWSECONNECT As Long = 55 = 56 Const SQL API-SQLCOLUMNPRIVILEGES As Long Const SQLAPI SQLDATASOURCES AS Long = 57 Const SQL-API-SQLDESCRIBEPARAM AS Long = 58 Const SQLAPI~SQLEXTENDEDFETCHAS Long = 59 = 60 Const SQLAPI SQLFOREIGNKEYS As Long Const SQLAPIZSQLMORERESULTS As Long = 61 = 62 Const SQL API SQLNATIVESQL As Long Const SQLAPIZSQLNUMPARAMS As Long = 63 = 64 Const S Q L A P I SQLPARAMOPTIONS As Long Const SQL-API-SQLPRIMARYKEYS As Long = 65 Const SQLAPI-SQLPROCEDURECOLUMNS As Long = 66 Const SQLAPI-SQLPROCEDURES As Long = 67 Const SQLAPI-SQLSETPOS As Long = 68 Const S Q L A P I ~ S Q L S E T S C R O L L O P T I O N S AS Long = 69 Const SQL-API-SQLTABLEPRIVILEGES As Long = 7 0 Const SQL-API-SQLDRIVERS As Long = 71 = 72 Const SQL API SQLBINDPARAMETER As Long " Global Const SQL-EXT-API-LAST As Long = 72 Global Const SQL-API-ALL-FUNCTIONS As Long = 0 Global Const SQL-NUM-EXTENSIONS As Long = (SQL-EXT-API-LAST - SQL-EXT-API-START + 1 ) ' Definiciones para SQLGetInfo I Global Global Global Global Global Global Global Global Global Global Global Const SQL-INFO-FIRST As Long = O Const SQL-ACTIVE-CONNECTIONS As Long = O Const SQL-ACTIVE-STATEMENTS As Long = 1 Const SQL-DATA-SOURCE-NAME As Long = 2 ConstSQL-DRIVER-HDBC As Long = 3 Const SQL-DRIVER-HENV As Long = 4 Const SQL-DRIVER-HSTMT As Long = 5 Const SQL-DRIVER-NAME As Long = 6 ConstSQL-DRIVER-VER As Long = 7 = 8 Const SQL-FETCH-DIRECTION As Long Const SQL-ODBC-API-CONFORMANCE As Long = 9 121 Global Global Global Global Global Global Const SQL-ODBC-VER As Long = 10 Const SQL-ROW-UPDATES As Long = 11 Const SQL-ODBC-SAG-CLI-CONFORMANCE As Long = 12 Const SQL-SERVER-NAME As Long = 13 Const SQL-SEARCH-PATTERN ESCAPE As Long= 14 Const SQL-ODBC-SQL-CONFO&ANANCE As Long = 15 Global Const SQL-DBMS-NAME As Long = 17 Global Const SQL-DBMS-VER As Long = 18 Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Const SQL-ACCESSIBLE-TABLES As Long = 19 Const SQL-ACCESSIBLE-PROCEDURES As Long = 20 Const SQL-PROCEDURES As Long = 21 = 22 Const SQL-CONCAT-NULL-BEHAVIOR As Long Const SQL-CURSOR-COMMIT-BEHAVIOR As Long = 23 Const SQL-CURSOR-ROLLBACK-BEHAVIOR As Long = 24 As Long = 25 Const SQL-DATA-SOURCE-READ_ONLY Const SQL-DEFAULT-TXN-ISOLATION As Long = 26 Const SQL-EXPRESSIONS-IN ORDERBY As Long= 27 Const SQL-IDENTIFIER-CASE As Long = 28 Const SQL-IDENTIFIER-QUOTE-CHAR As Long = 29 Const SQL-MAX-COLUMN-NAME-LEN As Long = 30 Const SQL-MAX-CURSOR-NAME-LEN As Long= 31 Const SQL-MAX-OWNER-NAME LEN As Long = 32 Const SQL-MAX-PROCEDURE iAME LEN As Long= 33 Const SQL-MAX-QUALIFIERINAMEILEN As Long = 34 Const SQL-MAX-TABLE-NAME LEN As Long = 35 Const SQL-MULT-RESULT-SETS As Long = 36 Const SQL-MULTIPLE-ACTIVE-TXN As Long = 37 = 38 Const SQL-OUTER-JOINS As Long Const SQL-OWNER-TERM As Long = 39 Const SQL-PROCEDURE-TERM As Long = 40 Const SQL QUALIFIER-NAME SEPARATOR As Long= 41 Const SQLIQUALIFIER-TERMAS Long = 42 Const SQL-SCROLL-CONCURRENCY As Long = 43 Const SQL-SCROLL-OPTIONS As Long = 44 Const SQL-TABLE-TERM As Long = 45 Const SQL-TXN-CAPABLE As Long = 46 Const SQL-USER-NAME As Long = 47 Global Global Global Global Global Const SQL-CONVERT-FUNCTIONS As Long = 48 Const SQL-NUMERIC-FUNCTIONS As Long = 49 Const SQL-STRING-FUNCTIONS As Long = 50 Const SQL-SYSTEM-FUNCTIONS As Long = 51 Const SQL-TIMEDATE-FUNCTIONS As Long= 52 Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global = 53 Const SQL-CONVERT-BIGINT As Long Const SQL-CONVERT-BINARY As Long = 54 Const SQL-CONVERT-BIT As Long = 55 Const SQL-CONVERT-CHAR As Long = 56 Const SQL-CONVERT-DATE As Long = 57 Const SQL-CONVERT-DECIMAL A s Long = 58 Const SQL-CONVERT-DOUBLE As Long = 59 Const SQL-CONVERT-FLOAT As Long = 60 Const SQL-CONVERT-INTEGER As Long = 61 Const SQL-CONVERT-LONGVARCHARAs Long = 62 Const SQL-CONVERT-NUMERIC As Long = 63 Const SQL-CONVERT-REAL As Long = 64 Const SQL-CONVERT-SMALLINT As Long = 65 Const SQL-CONVERT-TIME As Long = 66 Const SQL-CONVERT-TIMESTAMP As Long = 67 Const SQL-CONVERT-TINYINT As Long = 68 122 Global Const SQL-CONVERT-VARBINARY As Long = 69 Global Const SQL-CONVERT-VARCHAR As Long = 70 Global Const SQL-CONVERT-LONGVARBINARY As Long = 71 Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Const SQL-TXN-ISOLATION-OPTION As Long = 72 Const SQL-ODBC-SQL-OPT-IEF As Long = 73 Const SQL-CORRELATION-NAME As Long = 74 Const SQL-NON-NULLABLE-COLUMNS As Long = 75 Const SQL-DRIVER-HLIB As Long = 76 Const SQL-DRIVER-ODBC-VER As Long = 77 Const SQL-LOCK-TYPES As Long = 78 Const SQL-POS-OPERATIONS As Long = 79 Const SQL-POSITIONED-STATEMENTS As Long = 80 Const SQL GETDATA-EXTENSIONS As Long= 81 Const SQL~BOOKMARK-PERSISTENCEAs Long = 82 Const SQL-STATIC-SENSITIVITY As Long = 83 Const SQL-FILE-USAGE As Long = 84 = 85 Const SQL-NULL-COLLATION As Long Const SQLALTER-TABLE As Long = 86 Const SQL-COLUMN-ALIAS As Long= 87 Const SQL-GROUP-BY As Long = 88 Const SQL-KEYWORDS As Long = 89 Const SQL-ORDER-BY-COLUMNS-IN-SELECT As Long = 90 Const SQL-OWNER-USAGE As Long = 91 = 92 Const SQL QUALIFIER-USAGE As Long Const SQLlQUOTED-IDENTIFIER-CASE As Long = 93 Const SQL-SPECIAL-CHARACTERS As Long = 94 Const SQL-SUBQUERIES As Long= 95 Const SQL-UNION As Long = 96 Const SQL-MAX-COLUMNS-IN-GROUP-BY As Long = 97 Const SQL-MAX-COLUMNS-IN-INDEX As Long = 98 As Long = 99 Const SQL-MAX-COLUMNS-IN-ORDER_BY Const SQL-AX-COLUMNS-IN-SELECT As Long = 100 Const SQL-MAX-COLUMNS-IN-TABLE As Long = 1 0 1 Const SQL-MAX-INDEX-SIZE As Long= 102 Const SQL-MAX-ROW-SIZE-INCLUDES-LONG As Long = 103 Const SQL-MAX-ROW-SIZE As Long = 104 Const SQLMAX STATEMENT-LEN As Long = 105 Const SQL-MAXITABLES-IN-SELECT As Long = 106 Const SQLIMAX-USER-NAME-LEN As Long = 107 Const SQL-MAX-CHAR-LITERAL-LEN As Long = 108 Const SQL-TIMEDATE-ADD-INTERVALS As Long = 109 Const SQL-TIMEDATE-DIFF-INTERVALS As Long = 110 Const SQL-NEED-LONG-DATA-LEN As Long = 1 1 1 Const SQL MAX-BINARY-LITERAL-LEN As Long = 112 Const SQLILIKE-ESCAPE-CLAUSE As Long = 113 Const SQL-QUALIFIER-LOCATION As Long = 114 = SQL-QUALIFIER-LOCATION Global Const SQL-INFO-LAST As Long Global Const SQL-INFO-DRIVER-START As Long = 1 0 0 0 Máscaras de bits para "SQL-CONVERT-" I Global Global Global Global Global Global Global Global Const SQL-CVT-CHAR As Long = &HI& = &HZ& Const SQL-CVT-NUMERIC As Long Const SQL-CVT-DECIMAL As Long = &H4& Const SQL-CVT-INTEGER As Long = &Ha& Const SQL-CVT-SMALLINT As Long = &H10& Const SQL-CVT-FLOAT As Long = &H20& Const SQL-CVT-REAL As Long = &H40& Const SQL-CVT-DOUBLE As Long = &H8O& 123 Global Global Global Global Global Global Global Global Global Global Global Const SQL-CVT-VARCHAR As Long = &H100& Const SQL-CVT-LONGVARCHAR As Long = &H200& Const SQL-CVT-BINARY As Long = &H400& = &H800& Const SQL-CVT-VARBINARY As Long Const SQL-CVT-BIT As Long = &H1000& Const SQL-CVT-TINYINT As Long = &H2000& Const SQL-CVT-BIGINT As Long = &H4000& Const SQL-CVT-DATE As Long = &H8000& Const SQL-CVT-TIME As Long = &H10000 Const SQL-CVT-TIMESTAMP As Long= &H20000 Const SQL-CVT-LONGVARBINARY As Long = &H40000 ' Funciones de conversión , Global Const SQL -FN-CVT-CONVERT As Long= &H1& ' Funciones de cadena 1 Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Const SQL-FN-STR-CONCAT As Long = &H1& Const SQL-FN-STR-INSERT As Long= &H2& Const SQL-FN-STR-LEFT As Long = &H4& Const SQL-FN STR-LTRIM As Long= &H8& Const SQL-FNISTR-LENGTH As Long = &H10& Const SQL-FN-STR-LOCATE As Long = &H20& Const SQL-FN-STR-LCASE As Long = &H40& Const SQL-FN-STR-REPEAT As Long = &H80& Const SQLFN-STR-REPLACE As Long = &H100& Const SQLIFN-STR-RIGHT As Long = &H200& Const SQL FN-STR-RTRIM As Long = &H400& = &H800& Const SQLIFN STR SUBSTRING As Long Const SQL FNISTRIUCASE As Long= &H1000& Const SQLIFN-STR-ASCII As Long = &H2000& Const SQL-FN-STR-CHAR As Long = &H4000& Const SQL-FN-STR-DIFFERENCE As Long= &H8000& Const SQL-FN-STR-LOCATE 2 As Long= &H10000 Const SQL-FN-STR-SOUNDEX As Long = &H20000 Const SQL-FN-STR-SPACE As Long = &H40000 ' Funciones numéricas I Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Const SQL-FN-NUM-ABS As Long = &H1& Const SQL-FN-NUM-ACOS As Long = &H2& Const SQL-FN-NUM-ASIN As Long = &H4& Const SQL-FN-NUM-ATAN As Long = &H8& Const SQL-FN-NUM-ATAN2 As Long = &H10& Const SQL-FN-NUM-CEILING As Long = &H20& Const SQL-FN-NUM-COS As Long = &H40& Const SQL-FN-NUM-COT As Long = &H80& Const SQL-FN-NUM-EXP As Long = &H100& Const SQL-FN-NUM-FLOOR As Long= &H200& Const SQL-FN-NUM-LOG As Long = &H400& Const SQL-FN-NUM-MOD As Long = &H800& Const SQL-FN-NUM-SIGN As Long = &H1000& Const SQL-FN-NUM-SIN As Long = &H2000& Const SQL-FN-NUM-SQRT As Long = &H4000& Const SQL-FN-NUM-TAN As Long = &H8000& Const SQL-FN-NUM-PI As Long = &H10000 Const SQL FN-NUM-RAND As Long = &H20000 Const SQLIFN-NUM-DEGREES As Long = &H40000 Const SQL-FN-NUM-LOG10 As Long = &H80000 124 Global Global Global Global Const SQL-E'N-NUM-POWER As Long = &H100000 Const SQL-FN-NUM-RADIANS As Long = &H200000 Const SQL-FN-NUM-ROUND As Long = &H400000 Const SQL-FN-NUM-TRUNCATE As Long = &H800000 ' Funciones de fecha y hora 1 Global Global Global Global Global Global Global Global Global Global Global Global Global Const SQL-FN-TD-NOW As Long = &HI& Const SQL-FN-TD-CURDATE As Long = &H2& = &H4& Const SQL-FN-TD-DAYOFMONTH As Long Const SQL-FN-TD-DAYOFWEEK As Long = &HE& Const SQL-FN-TD-DAYOFYEAR As Long = &H10& Const SQL-E'N-TD-MONTH As Long = &H20& Const SQL-FN-TD-QUARTER As Long = &H40& Const SQL-FT-TD-WEEK As Long = &H80& Const SQL-FN-TD-YEAR As Long = &H100& Const SQL-FN-TD-CURTIME As Long = &H200& Const SQL-FN-TD-HOUR As Long = &H400& Const SQL-FN-TD-MINUTE As Long = &H800& Const SQL-FN-TD-SECOND As Long = &H1000& I Global ConstSQL-FN-TD-TIMESTAMPADD As Long = &H2000& Global Const SQL-FN-TD-TIMESTAMPDIFFAs Long = &H4000& Global Const SQL-FN-TD-DAYNAME As Long = &H8000& Global ConstSQL-FN-TD-MONTHNAME As Long= &H10000 ' Funciones del sistema 1 Global Const SQL-FN-SYS-USERNAME As Long = &H1& Global Const SQL-FN,-SYS-DBNAME As Long= &H2& Global Const SQL-FN-SYS-IFNULL As Long = &H4& Intervalos de fecha y hora I I Global Global Global Global Global Global Global Global Global Const SQL-FN-TSI-FRAC-SECOND As Long = &H1& Const SQL-FN-TSI-SECOND As Long = &H2& Const SQL-FN-TSI-MINUTE As Long = &H4& Const SQL-FN-TSI-HOUR As Long = &H8& Const SQL-FN-TSI-DAY As Long = &H10& Const SQL-FN-TSI-WEEK As Long = &H20& Const SQL-FN-TSI-MONTH As Long= &H40& Const SQLFN-TSI-QUARTER As Long = &H80& Const SQLIFN-TSI-YEAR As Long = &H100& acomodación ODBC-API 1 Global Const SQL-OAC-NONE As Long = O Global Const SQL-OAC-LEVEL1 As Long = 1 Global Const SQL-OAC-LEVEL2 As Long = 2 ' acomodación SAG-CLI I Global Const SQL-OSCC-NOT-COMPLIANT As Long = O Global Const SQL-OSCC-COMPLIANT As Long= 1 ' acomodación ODBC-SQL 1 Global Const SQL-OSC-MINIMUM As Long= O 125 Global Const SQL-OSC-CORE As Long = 1 Global Const SQL-OSC-EXTENDED As Long = 2 ' Comportamiento en concatenacih I Global Const SQL-CB-NULL As Long = O Global Const SQL-CB-NON-NULL As Long= 1 ' Comportamiento del cursor I Global Const SQL-CB-DELETE As Long = O Global Const SQL-CB-CLOSE As Long = 1 Global Const SQL-CB-PRESERVE As Long = 2 ' Identificación de las letras 1 Global Global Global Global Const SQL-IC-UPPER As Const SQL-IC-LOWER As Const SQL-IC-SENSITIVE Const SQL-IC-MIXED As Long = 1 Long = 2 As Long Long = 4 = 3 ' Transaction capable I Global Const SQL-TC-NONE As Long = O Global Const SQL-TC-DML As Long = 1 Global Const SQL-TCALL As Long = 2 1 Global Const SQL-TC-DDL-COMMIT As Long Global Const SQL-TC-DDL-IGNORE As Long = = 3 4 ' Capacidad de transacción I Global Global Global Global Const SQL-SO-FORWARD-ONLY As Long = &H1& Const SQL-SO-KEYSET-DRIVEN As Long= &HZ& Const SQL-SO-DYNAMIC As Long = &H4& Const SQL-SO-MIXED As Long = &H8& 1 Global Const SQL-SO-STATIC As Long = &H10& ' Máscaras de optimización de desplazamiento I Global Global Global Global Const SQL-SCCO-READ-ONLY As Long = &H1& Const SQL-SCCO-LOCK As Long = &H2& Const SQL-SCCO-OPT-ROWVER As Long = &H4& Const SQL-SCCO-OPT-VALUES As Long = &H8& ' Traer máscaras de opciones de dirección 1 Global Global Global Global Global Global Global Const SQL-FD-FETCH-NEXT As Long = &H1& Const SQL-FD-FETCH-FIRST As Long = &H2& Const SQL-FD-FETCH-LAST As Long = &H4& Const SQL-FD-FETCH-PRIOR As Long = &H8& Const SQL-FD-FETCH-ABSOLUTE As Long = &H10& Const SQL-FD-FETCH-RELATIVE As Long = &H20& Const SQL-FD-FETCH-RESUME As Long = &H40& I Global Const SQL-FD-FETCH-BOOKMARK As Long = &H80& ' Máscaras de opción de transacción para aislamiento 126 I Global Global Global Global Global Const SQL-TXN-READ-UNCOMMITTED As Long = &H1& Const SQL-TXN-READ-COMMITTED As Long = &H2& Const SQL-TXN-REPEATABLE-READ As Long = &H4& Const SQL-TXN-SERIALIZABLE As Long = &H8& Const SQL-TXN-VERSIONING As Long = &H10& ' Nombre de correlación 1 Global Const SQL-CNNONE As Long = O Global Const SQL-CN-DIFFERENT As Long = 1 Global Const SQL-CN-ANY As Long = 2 Columnas queno permiten nulos 1 Global Const SQL-NNC-NULL As Long = O Global Const SQL-NNC-NON-NULL As Long = 1 I ' Ordenación de nulos 1 Global Global Global Global ' Const Const Const Const SQL-NC-HIGH As Long = O SQL-NC-LOW As Long = 1 SQL-NC-START As Long = 2 SQL-NC-END As Long = 4 Uso de archivo I Global ConstSQL-FILE-NOT-SUPPORTED As Long = O Global Const SQL-FILE-TABLE As Long = 1 Global Const SQL-FILE-QUALIFIER As Long = 2 ' Máscaras para extensiones SQLGetData 1 Global Global Global Global Const SQL-GD-ANY-COLUMN As Long = &HI& Const SQL-GDANY-ORDER As Long = &H2& Const SQL-GD-BLOCK As Long = &H4& Const SQL-GD-BOUND As Long = &HE& ' Modificar una tabla 1 Global Const SQL-AT-ADD-COLUMN As'Long = 1 Global ConstSQL-AT-DROP-COLUMN As Long = 2 ' Máscaras para instruccionesde posicionamiento I Global Const SQL-PS-POSITIONED-DELETE As Long = &HI& Global Const SQL PS-POSITIONED-UPDATE As Long = &H2& Global Const SQLIPS-SELECT-FOR-UPDATE As Long = &H4& Ordenar por I Global ConstSQL-GB-NOT-SUPPORTED As Long = O Global Const SQL-GB-GROUP-BY-EQUALS_SELECT As Long = 1 Global Const SQL-GB-GROUP-BY-CONTAINS-SELECT As Long = 2 Global Const SQL-GB-NO-RELATION As Long = 3 , Máscaras de u s o de propietario Global Const SQL-OU-DML-STATEMENTS As Long = &H1& Global Const SQL OU PROCEDURE-INVOCATION As Long= &H2& As Long = &H4& Global Const SQL~OU~TABLE-DEFINITION 127 Global Const SQL-OU-INDEX-DEFINITION As Long = &H8& Global Const SQL-OU-PRIVILEGE-DEFINITION As Long = &H10& ' Máscaras de usuario cualificado , Global Global Global Global Global Const SQL-QU-DML-STATEMENTS As Long = &H1& = &H2& Const SQL-QU-PROCEDURE-INVOCATION As Long Const SQL-QU-TABLE-DEFINITION As Long = &H4& Const SQL-QU-INDEX-DEFINITION As Long = &H8& Const SQL-QU-PRIVILEGE-DEFINITION As Long = &H10& ' Máscaras de subconsultas I Global Global Global Global Global Const SQL-SQ-COMPARISON As Long = &H1& Const SQL-SQ-EXISTS As Long = &H2& Const SQL-SQ-IN As Long = &H4& Const SQL-SQ-QUANTIFIED As Long = &Ha& Const SQL-SQ-CORRELATED-SUBQUERIES As Long = &H10& Máscaras de unión 1 Global Const SQL U-UNION As Long = &H1& Global Const SQLIU-UNION-ALL As Long = &H2& ' Persistencia de Bookmark 1 Global Global Global Global Global Global Global Const SQL-BP-CLOSE As Long = &H1& Const SQL-BP-DELETE As Long = &H2& Const SQL-BP-DROP As Long = &H4& Const SQL BP-TRANSACTION As Long = &H8& Const SQL-BP UPDATE As Long= &H10& Const SQLBPIOTHER-HSTMT As Long = &H20& = &H40& Const SQL-BP-SCROLL As Long ' Sensibilidad estática 1 Global Const SQL-SS-ADDITIONS As Long = &HI& Global Const SQL-SS-DELETIONS As Long = &H2& Global Const SQL-SS-UPDATES As Long = &H4& ' Máscaras de bloqueo de tipos 1 Global Const SQL LCK NO-CHANGE As Long = &H1& Global Const SQL-LCK-EXCLUSIVE As Long = &H2& Global Const SQL~LCK~UNLOCK As Long = &H4& Máscaras de operaciones de posicionamiento 1 Global Global Global Global Global I Const SQL-POS-POSITION As Long = &H1& Const SQL-POS-REFRESH As Long = &H2& Const SQLPOS UPDATE As Long= &H4& Const SQLIPOSIDELETE As Long = &H8& Const SQL-POS-ADD As Long = &H10& Calificar la localización 1 Global Const SQL-QL-START As Long = 1 Global Const SQL-QL-END As Long = 2 Opciones para SQLGetStmtOption/SQLSetStmtOption I Global Const SQL -QUERY-TIMEOUT As Long = O 128 Global Global Global Global Global Global Global Global Global Global Global Global Global Global Const SQL-"ROWS As Long = 1 Const SQL-NOSCAN As Long = 2 Const SQL-MAX-LENGTH As Long = 3 Const SQLASYNC-ENABLE As Long = 4 Const SQL-BIND-TYPE As Long = 5 Const SQL-CURSOR-TYPE As Long = 6 Const SQL-CONCURRENCY As Long= I Const SQL-KEYSET-SIZE As Long = 8 Const SQL-ROWSET-SIZE As Long = 9 Const SQL-SIMULATE-CURSOR As Long = 10 Const SQL-RETRIEVE-DATA As Long = 11 Const SQL-USE-BOOKMARKS As Long = 1 2 Const SQL-GET-BOOKMARK As Long = 13 Const SQL-ROW-NUMBER As Long = 14 Global Const SQL-STMT-OPT-MAX As Long = SQL-ROW-NUMBER ' Opciones y valores predeterminadospara instrucciones I Global Const SQL-QUERY-TIMEOUT-DEFAULT As Long = O Global Const SQL-MAX-ROWS-DEFAULT As Long= O Global Const SQL-NOSCAN-OFF As Long = O Global Const SQL-NOSCAN-ON As Long= 1 Global Const SQL-NOSCAN-DEFAULT As Long = SQL-NOSCAN-OFF Global Const SQL-MAX-LENGTH-DEFAULT As Long = O Global Const SQL-ASYNC-ENABLE-OFF As Long = O Global Const SQL-ASYNC-ENABLE-ON As Long = 1 Global Const SQL-ASYNC-ENABLE-DEFAULT As Long = SQL-ASYNC-ENABLE-OFF Global Const SQL-BIND-BY-COLUMN As Long Global Global Global Global Global Global Global Global = 0 Const SQL-CONCUR-READ-ONLY As Long = 1 Const SQL-CONCUR-LOCK As Long= 2 Const SQL-CONCUR-ROWVER As Long = 3 Const SQL-CONCUR-VALUES As Long = 4 Const SQL-CURSOR-FORWARD-ONLYAs Long = O Const SQL-CURSOR-KEYSET-DRIVEN As Long = 1 Const SQL-CURSOR-DYNAMIC As Long = 2 Const SQL-CURSOR-STATIC As Long = 3 Global Const SQL-ROWSET-SIZE-DEFAULT As Long = 1 Global Const SQL-KEYSET-SIZE-DEFAULT As Long = O Global Const SQL-SC-NON-UNIQUE As Long = O Global Const SQL-SC-TRY-UNIQUE As Long = 1 Global Const SQL-SC-UNIQUE As Long = 2 Global Const SQL-RD-OFF As Long = O Global Const SQL-RD-ON As Long = 1 Global Const SQL-RD-DEFAULT As Long = SQL-RD-ON Global Const SQL-UB-OFF As Long = O Global Const SQL-UB-ON As Long = 1 Global Const SQL-UB-DEFAULT As Long = SQL-UB-ON ' Opciones para SQLSetConnectOption/SQLGetConnectOption 129 Global Global Global Global Global Global Global Global Global Global Const SQL-ACCESS-MODE As Long = 101 Const SQL-AUTOCOMMIT As Long = 102 Const SQL-LOGIN-TIMEOUT As Long = 103 Const SQL-OPT-TRACE As Long = 104 Const SQL-OPT-TRACEFILE As Long = 105 Const SQL-TRANSLATE-DLL As Long = 106 Const SQL-TRANSLATE-OPTION As Long = 107 Const SQL-TXN-ISOLATION As Long = 108 Const SQL-CURRENT-QUALIFIER As Long = 109 Const SQL-CONNECT-OPT-DRVR-START As Long = 1000 I Global Const SQL-ODBC-CURSORS As Long = 110 Global Const SQL-QUIET-MODE As Long = 111 Global Const SQL-PACKET-SIZE As Long= 112 Global Const SQL-CONN-OPT-MAX As Long Global Const SQL-CONN OPT MIN " As Long = SQL-PACKET-SIZE = SQLACCESS-MODE Opciones de modo de acceso 1 Global Const SQL-MODE-READ WRITE As Long = O Global Const SQL-MODE-READIONLY As Long = 1 Global ConstSQL-MODE-DEFAULT As Long= SQL-MODE-READ -WRITE ' Opciones para ejecución automática de transacciones 1 Global Const SQL-AUTOCOMMIT-OFF As Long = O Global Const SQL-AUTOCOMMIT-ON As Long = 1 Global Const SQL-AUTOCOMMIT-DEFAULT As I = Long SQL-AUTOCOMMIT-ON Opciones para el tiempo límite de inicio de sesión I Global Const SQL-LOGIN-TIMEOUT-DEFAULT As Long = 15 ' Opciones de traza 1 Global Global Global Global Const SQL-OPT-TRACE-OFF As Long = O Const SQL-OPT-TRACE-ON As Long = 1 Const SQL-OPT TRACE DEFAULT As Long= SQL-OPT TRACE-OFF Const SQL-OPTITRACEZFILE-DEFAULT = "\\SQL.LOG" 1 ' Opciones del cursor I Global Const SQL-CUR-USE IF NEEDED As Long= O Global Const SQL-CUR USEIODBC As Long = 1 Global Const SQL-CURIUSE-DRIVER As Long = 2 Global ConstSQL-CUR-DEFAULT As Long= SQL-CUR-USE-DRIVER ' Tipos de columna y alcance en SQLSpecialColumns. 1 Global Const SQL-BEST-ROWID As Long Global Const SQL-ROWVER As Long = 2 = 1 Global Const SQL-SCOPE-CURROW As Long = O Global Const SQL-SCOPE-TRANSACTION As Long = 1 Global Const SQL-SCOPE-SESSION As Long = 2 I I Funciones de nivel 2 130 Valores SQLExtendedFetch "fFetchType" I Global Global Global Global Global Global Global Const SQL-FETCH-NEXT As Long = 1 Const SQL-FETCH-FIRST As Long = 2 Const SQL-FETCH-LAST As Long = 3 Const SQL-FETCH-PRIOR As Long = 4 Const SQL-FETCH-ABSOLUTE As Long = 5 Const SQL-FETCH-RELATIVE As Long = 6 Const SQL-FETCH-BOOKMARK As Long = 8 ' Valores SQLExtendedFetch "rgfRowStatus" I Global Global Global Global Const Const Const Const SQL-ROW-SUCCESS SQL-ROW-DELETED SQL-ROW-UPDATED SQL NOROW - ROW - As Long = O As Long = 1 As Long = 2 As Long = 3 1 Global Const SQL-ROW-ADDED As Long Global Const SQL-ROW-ERROR As Long = = 4 5 Definiciones para SQLForeignKeys (devuelto el en conjunto de resultados) I Global Const SQL-CASCADE As Long = O = 1 Global Const SQL RESTRICT As Long Global Const SQLISET-NULL As Long= 2 ' Definiciones para SQLProcedureColumns (devuelto en el conjunto de resultados) I Global Global Global Global Const SQL-PARAM-TYPE-UNKNOWN As Long = O Const SQL-PARAM-INPUT As Long = 1 Const SQL-PARAM-INPUT-OUTPUT As Long = 2 Const SQL-RESULT-COL As Long = 3 I Global Const SQL-PARAM-OUTPUT As Long = 4 Definiciones para SQLStatistics 1 Global Global Global Global Const SQL-INDEX-UNIQUE As Long = O Const SQL-INDEX-ALL As Long = 1 Const SQL-ENSURE As Long = 1 Const SQL-QUICK As Long = O Definiciones para SQLStatistics (devuelto en el conjunto de resultados) I Global Global Global Global Const SQL-TABLE-STAT As Long = O Const SQL-INDEX-CLUSTERED As Long = 1 Const SQL-INDEX-HASHED As Long = 2 Const SQL-INDEX-OTHER As Long = 3 I , Procedimientos Global Const SQL-PT-UNKNOWN As Long = O Global Const SQL-PT.-PROCEDURE As Long = 1 Global Const SQL-PT-FUNCTION As Long = 2 ' Procedimientos de columna 1 Global Const SQL-PC-UNKNOWN As Long = 0 131 Global Const SQL-PC-NON-PSEUDO As Long = 1 Global Const SQL-PC-PSEUDO As Long = 2 ' Definiciones para SQLSetPos 1 Global Const SQL-ENTIRE-ROWSET As Long Global Global Global Global Global = O Const SQL-POSITION As Long= O Const SQL-REFRESH As Long= 1 Const SQL-UPDATE As Long = 2 Const SQL-DELETE As Long = 3 Const SQL-ADD As Long = 4 ' Opciones de bloqueo I Global Const SQL-LOCK-NO-CHANGE As Long = O Global Const SQL-LOCK-EXCLUSIVE As Long = 1 Global Const SQL-LOCK-UNLOCK As Long = 2 ' Constantes globales de desacuerdo I Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Global Const SQL-DATABASE-NAME As Long = 16 Const SQL-FD-FETCH-PREV As Long = SQL-FD-FETCH-PRIOR Const SQL-FETCH-PREV As Long = SQL-FETCH-PRIOR = SQL CONCUR ROWVER Const SQL CONCUR TIMESTAMP As Long Const SQL-SCCO-OFT-TIMESTAMP As Long = S~L-SCCO~OPT -ROWVER = SQL-CB DELETE Const SQL-CC DELETE As Long - . ConstSQL-CR-DELETE As Long = SQL CBIDELETE Const SQL-CC-CLOSE As Long = SQL-CB-CLOSE Const SQL-CR-CLOSE As Long = SQL-CB-CLOSE Const SQL-CC-PRESERVE As Long= SQL-CB PRESERVE Const SQL-CR-PRESERVE As Long = SQL-CB-PRESERVE Const SQLFETCH-RESUME As Long = 7 Const SQL-SCROLL FORWARD-ONLY As Long = O Const SQL-SCROLL~KEYSET-DRIVENAs Long = -1 Const SQLISCROLL-DYNAMIC As Long = -2 Const SQL-SCROLL-STATIC As Long = -3 #End If 'Win32 B Conexion.bas: Código de las funciones. El archivo Conexionhas es la API de Visual Basic para conexi6n a bases de datos. El Listado 14 muestra el contenidodelarchivo Conexionhas. Sepuedenapreciarlasdeclaracionesde API, así como el c6digo fuente de las funciones constantes y tipos de datos usados para esta incluidas en la misma. Listado 14. Listadodel módulo Conexion.bas. I""""""""""""""""- 'Sección de declaraciones 1""""""""""""""""- 'Constantes de uso general Global Const SQL MAX-ERROR-STRING Global Const MAXIROWS= 100 Global Const BufferRet= 256 = 255 'Valor de Loop para DoEvents Global ConstMAX-DOEVS = 30 'Valores de retorno de las funciones = -2 Global Const HANDLE-INVALID0 As Long Global Const ERROR-FUNC As Long = -1 Global Const EXITO-FUNC As Long = O Global ConstEXIT0 FUNC-CON-INFO As Long = 1 Global Const SIGUE-EJECUTANDO As Long = 2 = 99 Global Const NECESITA-DATOS As Long Global Const NO MAS DATOS As Long= 100 " _HAY 'Valores para AceptaNuloCol Global Const SIN-NULOS As Long = O Global Const CON-NULOS As Long = 1 Global ConstNO-DETERM As Long = 2 133 134 Private rc As Integer Public Type BaseDatos ENV As Long ID As Long NumTablas As Integer NomTablas ( ) As String End Type Private Type RegistroSQL NumRegAct As Integer Datoscol() As String End Type Public Type ConsultaSQL ID As Long InstSQL As String NumCols As Integer NumRegs As Long SQLTipoCol() As Integer NomTipoCol() As String AceptaNuloCol ( ) As Long NomColO As String Registro As RegistroSQL End Type I""""""""""""""""""""" 'Seccion de funciones 1""""""""""" Public Sub CerrarInstrucSQL(CSQL As ConsultaSQL) 'Liberar todoslos handles y desconectar todo If CSQL.ID <> SQL-NULL-HSTMT Then rc = SQLFreeStmt(CSQL.ID, SQL-CLOSE) 'Libera el handle de sentencia Select Case rc Case SQL-SUCCESS Case SQL-SUCCESS-WITH-INFO DescErrorODBC SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, True Case SQL-ERROR DescErrorODBC SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, True Exit Sub End Select rc = SQLFreeStmt(CSQL.ID, SQL-DROP) 'Libera l o s recursos asignadosal handle Select Case rc Case SQL-SUCCESS Case SQL SUCCESS-WITH-INFO DescErrorODBC SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, True Case SQL-ERROR DescErrorODBC SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, True Exit Sub End Select CSQL.ID = SQL-NULL-HSTMT 'Asignar el valor nulo al handle de instruccian End I f End Sub Public Function EjecutarSQL(BD As BaseDatos, ByVal SQL$, ByVal MostrarError,CadErrorS) As Integer Dim Cr As String Cr = Chr(l3) & Chr(l0) Dim lrc As Integer 135 Dim MsgActual As String Dim hstmtlocal As Long lrc = SQL-SUCCESS CadError = MsgActual = "I' "I' 'Inicializa el handle de instrucción para cada operación de escritura hstmtlocal = SQL-NULL-HSTMT 'Asigna un valor nulo al handle de instrucción 'Obtiene un nuevo handle de instrucción rc = SQLAllocStmt(BD.ID, hstmtlocal) 'Prueba los casos en los que puede caer rc Select Case rc Case SQL-SUCCESS lrc = rc Case SQL SUCCESS WITH-INFO CadError = DescErrorODBC (SQL-NULL-HENV, SQL-NULL-HDBC, hstmtlocal, MostrarError) lrc = rc Case SQL-ERROR CadError = DescErrorODBC(SQL-NULL-HENV, SQL- NULL- HDBC, hstmtlocal, MostrarError) lrc = rc EjecutarSQL = lrc Exit Function End Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 'Remite el Query rc = SQLExecDirect (hstmtlocal, SQL, Len ) (SQL) Select Case rc Case SQL-SUCCESS 'No lleva lrc = rc para no sobreescribir un SQL-SUCCESS-WITH-INFO Case SQL SUCCESS-WITH-INFO MsgAccual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, hstmtlocal,MostrarError) CadError = CadError & Cr & MsgActual lrc = rc Case SQL-NEED-DATA lrc = rc EjecutarSQL = lrc Exit Function Case SQL-ERROR MsgActual = DescErrorODBC(SQL_NULL-HENV, SQL-NULL -HDBC, hstmtlocal, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc EjecutarSQL = lrc Exit Function End Select I********************************************* 'Liberar todoslos handles y desconectar todo hstmtlocal <> SQL-NULL-HSTMT Then rc = SQLFreeStmt(hstmtloca1, SQL-CLOSE) 'Libera el handle de sentencia Select Case rc Case SQL-SUCCESS un 'No lleva lrc= rc para no sobreescribir SQL-SUCCESS-WITH-INFO Case SQL-SUCCESS-WITH-INFO MsgActual = DescErrorODBC(SQL_NULL-HENV, SQL-NULL-HDBC, hstmtlocal, MostrarError) If 136 CadError = CadError & Cr & MsgActual lrc = rc Case SQL-ERROR MsgActual = DescErrorODBC(SQLJULL-HENV, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc EjecutarSQL = lrc Exit Function End Select 'Libera los recursos asignados al handle rc = SQLFreeStmt(hstmtloca1, SQL-DROP) Select Case rc Case SQL-SUCCESS 'No lleva lrc= rc para no sobreescribir Case SQL-SUCCESS-WITH-INFO MsgActual = DescErrorODBC(SQL-NULL-HENV, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL-HENV, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc EjecutarSQL = lrc Exit Function End Select hstmtlocal = SQL-NULL-HSTMT 'Asignar el valor End If EjecutarSQL = SQL-NULL-HDBC, hstmtlocal, - unSQL-SUCCESS-WITH-INFO SQL-NULL-HDBC, hstmtlocal, - SQL-NULL-HDBC, hstmtlocal, - nulo al handle de instrucción lrc End Function Public Function ErroresNativos(ByVa1 MsgErrorS) As String Dim ErrNat As String Dim PosCad As Integer, Inicio As Integer, Longitud As Integer ErrNat = " " Inicio = 1 If MsgError <> '"' Then Do PosCad = InStr(Inici0, MsgError, "Error nativo: ", O ) I f PosCad > O Then Inicio = PosCad + 1 4 PosCad = InStr (Inicio, MsgError, " "r 0) Longitud = PosCad - Inicio ErrNat = ErrNat + " / " f Mid(MsgError, Inicio, Longitud) End If Loop Until PosCad= O End If ErroresNativos = ErrNat End Function Public Function DescErrorODBC(ByVa1 rhenv&, ByVal rhdbc&, ByVal rhstmt&, ByVal MostrarErr) As String Const LngBuffSalida= SQL-MAX-ERROR-STRING = "Error del controlador ODBC" Const TitError As String 137 Dim Cr$ Dim CrLfS Cr$ = Chr$ (13) CrLf$ = ChrS(13) & ChrS(10) Dim CadDescCorta As String* 16 Dim CadDescExt As String * LngBuffSalida Dim LngDescrip As Integer Dim ErrNativo As Long Dim lrc As Integer CadDescCorta = String(l6, O) CadDescExt = String(LngBuffSalida, O) Dim lhenv As Long, lhdbc As Long, lhstmt As Long Dim MsgActualAs String Dim CadResultErr As String MsgActual = CadResultErr = For L = 1 To 3 I"' '"I Select Case L Case 1 lhenv = SQL-NULL-HENV lhdbc = SQL-NULL-HDBC lhstmt = rhstmt Case 2 lhenv = SQL-NULL-HENV lhdbc = rhdbc lhstmt = SQL-NULL-HSTMT Case 3 lhenv = rhenv lhdbc = SQL-NULL HDBC lhstmt = SQL-NULZ-HSTMT End Select 'Obtener todosl o s errores de el buffer de errores Do lrc = SQLError(lhenv, lhdbc, lhstmt, CadDescCorta, ErrNativo, CadDescExt, LngBuffSalida, LngDescrip) If lrc = SQL SUCCESS Or lrc= SQL-SUCCESS-WITH-INFO Then If LngDescrip = O Then MsgActual = "Error -- No hay información disponible" If MostrarErr Then MsgBox MsgActual, vbExclamation, TitError CadResultErr = CadResultErr & CrLf & MsgActual Else If ErrNativo= O Then MsgActual = Left$(CadDescExt, LngDescrip) I f MostrarErr Then MsgBox MsgActual, vbExclamation, TitError CadResultErr = CadResultErr & CrLf & MsgActual Else MsgActual = Left$(CadDescExt, LngDescrip)& Cr MsgActual = MsgActual & "Error nativo: " & ErrNativo & '' If MostrarErrThen MsgBox MsgActual, vbcritical, TitError CadResultErr = CadResultErr & CrLf & MsgActual End If End If End I f Loop Until lrc<> SQL-SUCCESS 'I Next L DescErrorODBC End Function = CadResultErr 138 Public Sub DesconectarBD(BD As BaseDatos) I f BD.ID <> SQL NULL-HDBC Then (BD. ID) 'Desconecta de la BD rc = SQLDiscGnnect Select Case rc Case SQL-SUCCESS Case SQL-SUCCESS-WITH-INFO DescErrorODBC SQL-NULL-HENV, BD.ID, SQL-NULL-HSTMT, True Case SQL-ERROR DescErrorODBC SQL-NULL-HENV, BD.ID, SQL-NULL-HSTMT, True Exit Sub End Select rc = SQLFreeConnect(BD.ID) 'Libera el handlede conexión y los recursos Select Case rc Case SQL-SUCCESS Case SQL SUCCESS-WITH-INFO DescErrorODBC SQL-NULL-HENV, BD. ID, SQL-NULL-HSTMT, True Case SQL-ERROR DescErrorODBC SQL-NULL-HENV, BD.ID, SQL- NULL-HSTMT, True Exit Sub End Select BD.ID = SQL-NULL-HDBC 'Asignar el valor nulo al handle de conexión End If If BD.ENV <> SQL-NULL-HENV Then (BD. ENV) 'Libera el handler de ambiente rc = SQLFreeEnv Select Case rc Case SQL SUCCESS Case SQL-SUCCESS-WITH-INFO DescEgrorODBC BD.ENV, SQL HDBC, -SQL NULL-HSTMT, True - NULL Case SQL ERROR DescErrorODBC BD.ENV, SQL-NULL-HDBC, SQL- NULL-HSTMT, True Exit S u b End Select de ambiente BD.ENV = SQL-NULL-HENV 'Asignar el valor nulo al handle End If End Sub Public Function ConectarBD (ByVal DSN$, ByVal UIDS, ByVal PWD$, BD As BaseDatos,ByVal MostrarErr, CadErroresS) As Integer 'NOTA: Pasar por referencia una estructura en la que se devuelvan los datos 'relacionados con la BD, esto es Tablas, Driver,etc. 'Realizar las conexiones necesarias a la BD 'y todas las inicializaciones requeridas Dim Conecta As String * 255 Dim ConexionAbierta As String Dim LongitudCA As Integer CadErrores = 'Obtiene un handle para el ambiente I f BD.ENV <> SQL-NULL-HENV Then rc = SQLFreeEnv(BD.ENV) BD.ENV = SQL-NULL-HENV End I f rc = SQLAllocEnv(BD.ENV) I"' 'Inicializa el handlede conexión If BD.ID <> SQL NULL-HDBC Then 'Desconecta de la BD ID) rc = SQLDisconnect (BD. 'Libera la conexión rc = SQLFreeConnect(BD.ID) BD.ID = SQL-NULL-HDBC 139 End If rc = SQLAllocConnect(BD-ENV,BD.ID) 'Establecer las opciones de conexión 'rc = SQLSetConnectOption(BD.ID, SQL-ACCESS-MODE, SQL MODE-READ-WRITE) rc = SQLSetConnectOption(BD.ID, SQL-ODBC-CURSORS, SQLICUR USE-ODBC) ' rc = SQLSetConnectOption (BD. SQL-TXN-ISOLATION, ID, SQL T?N REPEATABLE-READ) " 'BD.ID rc = = hdbc SQLConnect (BD. ID, DSN, Len (DSN1 , UID, Len(UID), PWD, Len (PWD) ) 'Checar la info del error 'rc puede serSQL-SUCCESS-WITH-INFO ' o SQL-ERROR y en estos casos hay información Select Case rc Case SQL-SUCCESS 'BD.ID = hdbc Case SQL SUCCESS- WITH - INFO 'BD.I f = hdbc CadErrores = DescErrorODBC(BD.ENV, BD.ID, SQL-NULL-HSTMT, MostrarErr) Case SQL-ERROR 'BD.ID = SQL-NULL-HDBC CadErrores = DescErrorODBC(BD.ENV, BD.ID, SQL -NULL-HSTMT, MostrarErr) End Select 'Obtiene información de la conexión ConectarBD = rc End Function Public Function ConectarBD2(ByVal Cadcon$, BD As BaseDatos, ByVal MostrarErr, CadErroresS) As Integer 'NOTA: Pasar por referencia una estructura en la se que devuelvan los datos 'relacionados con l a BD, esto es Tablas, Driver, etc. 'Realizar las conexiones necesarias laa BD 'y todas las inicializaciones requeridas Dim Conecta As String Dim ConexionAbierta As String * 255 Dim LongitudCA As Integer CadErrores = " " 'Obtiene un handle para el ambiente If BD.ENV <> SQL-NULL-HENV Then rc = SQLFreeEnv(BD.ENV) BD.ENV = SQL-NULL-HENV End If rc = SQLAllocEnv(BD.ENV) 'Inicializa el handlede conexión If BD.ID <> SQL NULL HDBC Then rc = SQLDisc&nect(BD. ID) 'Desconecta de la BD la conexión rc = SQLFreeConnect(BD.ID) 'Libera BD.ID = SQL-NULL-HDBC End If rc = SQLAllocConnect(BD.ENV, BD.ID) 'Establecer las opciones de conexión rc = SQLSetConnectOption(BD.ID, SQL ACCESS MODE, SQLMODE-READ WRITE) (BD. ID, SQL-ODBC -CURSORS, SQz-CUR-USE-~DBC) rc = SQLSetConnectOption 140 'rc = SQLSetConnectOption(BD.ID, SQL-TXN-ISOLATION, SQL-TXN-REPEATABLE-READ) 'BD.ID = hdbc 'rc = SQLConnect(BD.ID, DSN,Len(DSN), UID, Len(UID), PWD, Len(PWD)) , ConexionAbierta, 255, rc = SQLDriverConnect(BD.ID, hWnd, CadCon, Len (CadCon) LongitudCA, SQL-DRIVER-COMPLETE) 'Checar lainfo del error 'rc puedeser SQL-SUCCESS-WITH-INFO o SQL-ERROR y en estos casos hay 'información Select Case rc Case SQL-SUCCESS 'BD.ID = hdbc Case SQL SUCCESS-WITH-INFO 'BD.I< = hdbc CadErrores = DescErrorODBC(BD.ENV, BD.ID, SQL-NULL-HSTMT, MostrarErr) Case SQL-ERROR 'BD.ID = SQL-NULL-HDBC CadErrores = DescErrorODBC(BD.ENV, BD.ID, SQL-NULL-HSTMT, MostrarErr) End Select 'Obtiene informaciónde la conexión ConectarBD2 = rc End Function Public Function HacerConsultaSQL(BD As BaseDatos, ByVal SQL$, CSQL As ConsultaSQL, ByVal MostrarError, CadErrorS) As Integer Dim CrAs String Cr = Chr (13) & Chr (10) Dim sValor As String* BufferRet Dim sSQL As String * BufferRet Dim lValor AsLong, iValor As Integer, larg2 As Long Dim TotalCols As Integer, I As Integer Dim lrc As Integer Dim MsgActualAs String lrc = SQL-SUCCESS CadError = MsgActual = 'Inicializa el handle de instrucción para cada consulta If CSQL.ID <> SQL NULL-HSTMT Then 'Cierra la consulta actual pero no libera el handle ID, SQL-CLOSE) rc = SQLFreeStmt (CSQL. 'Libera el handlede instrucción yS U S recursos rc = SQLFreeStmt (CSQL. SQL-DROP) ID, CSQL.ID = SQL-NULL-HSTMT 'Asigna un valor nulo al handle de instrucción End If 'Obtiene un nuevo handle de instrucción rc = SQLAllocStmt(BD.ID, CSQL.ID) 'Prueba l o s casos en l o s que puede caer rc Select Case rc Case SQL-SUCCESS lrc = rc Case SQL-SUCCESS-WITH-INFO -NULL-HDBC, CSQL CadError = DescErrorODBC(SQL-NULL-HENV, SQL MostrarError) lrc = rc Case SQL-ERROR CadError = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL I"' I"' 141 MostrarError) lrc = rc HacerConsultaSQL Exit Function End Select lrc = . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 'Ajusta las opciones de el handle de instrucción rc = SQLSetStmtOption(CSQL.ID, SQL- CURSOR - TYPE,SQL-CURSOR-STATIC) Select Case rc Case SQL-SUCCESS 'No lleva lrc = rc para no sobreescribir unSQL-SUCCESS-WITH-INFO Case SQL SUCCESS-WITH-INFO MsgAcrual = DescErrorODBC (SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc Case SQL-ERROR MsgActual = DescErrorODBC(SQL_NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc HacerConsultaSQL = lrc Exit Function End Select rc = SQLSetStmtOption(CSQL.ID, SQL-ROWSET-SIZE, MAX-ROWS) Select Case rc Case SQL-SUCCESS 'No lleva lrc= rc para no sobreescribir un SQL-SUCCESS-WITH-INFO Case SQL SUCCESS WITH-INFO MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL. ID, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL -HENV, SQL-NULL-HDBC, CSQL.ID, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc HacerConsultaSQL = lrc Exit Function End Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 'Asigna el valor del handle al ID de la Consulta y la instrucción SQL 'CSQL.ID = hstmt CSQL.InstSQL = SQL I********************************************* 'Remite el Query O) 'sSQL = String$ (BufferRet, 'sSQL = SQL ID, SQL, Len (SQL) ) rc = SQLExecDirect (CSQL. Select Case rc Case SQL-SUCCESS 'No lleva lrc= rc para no sobreescribir un SQL-SUCCESS-WITH-INFO Case SQL SUCCESS-WITHINFO MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL. ID, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc Case SQL -NEED-DATA lrc = rc HacerConsultaSQL = lrc Exit Function 142 Case SQL-ERROR MsgActual = DescErrorODBC(SQL_NULL-HENV, SQL-NULL-HDBC, CSQL-IDr MostrarError) CadError = CadError & Cr & MsgActual lrc = rc HacerConsultaSQL = lrc Exit Function End Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 'Obtiene el número de columnas de el resultadode el query rc = SQLNumResultCols(CSQL.ID, TotalCols) Select Case rc Case SQL-SUCCESS 'No lleva lrc= rc para no sobreescribir u11 SQL-SUCCESS-WITH-INFO Case SQL-SUCCESS-WITH-INFO MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQLeIDr MostrarError) CadError = CadError & Cr & MsgActual lrc = rc Case SQL-STILL-EXECUTING lrc = rc HacerConsultaSQL = Irc Exit Function Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL-HENVr SQL-NULL-HDBC, CSQL.ID, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc HacerConsultaSQL = lrc Exit Function End Select CSQL.NumCols = TotalCols . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 'Obtiene el número de renglones de la consulta CSQL.NumRegs = O Do ID) rc = SQLFetch (CSQL. Select Case rc Case SQL-SUCCESS 'No lleva lrc= rc para no sobreescribir unSQL-SUCCESS-WITH-INFO CSQL.NumRegs = CSQL.NumRegs + 1 Case SQL-SUCCESS-WITH-INFO MsgActual = DescErrorODBC(SQL-NULL-HENVr SQL-NULL-HDBC, CSQLeIDr MostrarError) CadError = CadError & Cr & MsgACtual lrc = rc CSQL.NumRegs = CSQL.NumRegs + 1 Case SQL-NO-DATA-FOUND Case SQL-STILL-EXECUTING lrc = rc HacerConsultaSQL = lrc Exit Function Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL_HENV, SQL-NULL-HDBC, CSQL.IDr MostrarError) CadError = CadError & Cr & MsgActual lrc = rc HacerConsultaSQL = lrc Exit Function End Select Loop Until rc = SQL-NO-DATA-FOUND 'Pone el cursor nuevamente enel primer renglón _" . 143 rc = RepUltimaConsulta(CSQL, MostrarError, MsgActual) Select Case rc Case SQL SUCCESS 'No lieva Irc= KC para no sobreescribir unSQL-SUCCESS-WITH-INFO Case SQL-SUCCESS-WITH-INFO CadErr = CadErr & Cr & MsgActual lrc = rc Case SQL-ERROR CadErr = CadErr & Cr & MsgActual lrc = rc HacerConsultaSQL = lrc Exit Function Case SQL-INVALID-HANDLE CadErr = CadErr & Cr & MsgActual lrc = rc HacerConsultaSQL = lrc Exit Function End Select .................................................................... 'Recupera información variadade las columnas ReDim CSQL.NomCol(1To TotalCols) As String ReDim CSQL.NomTipoCol(1To TotalCols) As String ReDim CSQL.SQLTipoCol(1To TotalCols) As Integer ReDim CSQL.AceptaNuloCol(1To TotalCols) As Long For I = 1 To TotalCols 'Recupera los encabezados de las tablas y los asigna al 'array de Nombres de Columnas O) sValor = String$ (BufferRet, iValor = O rc = SQLColAttributesString(CSQL.ID, I, SQL-COLUMN-LABEL, sValor, 255, ivalor, larg2) Select Case rc Case SQL-SUCCESS 'No lleva lrc= rc para no sobreescribir un SQL-SUCCESS-WITH-INFO CSQL.NomCo1(I) = Left$ (sValor, ivalor) Case SQL-SUCCESS-WITH-INFO SQL-NULL-HDBC, CSQL.ID, MsgActual = DescErrorODBC(SQL- NULL - HENV, MostrarError) CadError = CadError & MsgActual lrc = rc CSQL.NomCo1 (I) = Left$ (sValor, ivalor) Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarError) CadError = CadError & MsgActual lrc = rc HacerConsultaSQL = lrc Exit Function End Select 'Recupera los nombres de los tipos de datosde cada una de las columnas sValor = String$(BufferRet, O) iValor = O Select Case rc Case SQL-SUCCESS 'No lleva lrc= rc para no sobreescribir unSQL-SUCCESS-WITH-INFO CSQL.NomTipoCo1 (I) = Left$ (sValor, iValor) Case SQL SUCCESS WITH-INFO MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL. ID, MostrarError) CadError = CadError & MsgActual 144 lrc = rc ~ ~ ~ ~ . N o m T i p(I) o C o= lLeft$ (svalor, iValor) MostrarError) CadError = CadError & MsgActual lrc = rc HacerConsultaSQL = lrc Exit Function End Select 'Recupera l o s tipos de datos SQL de cada una de las columnas sValor = String$ (BufferRet, 0) larg2) Select Case rc Case SQL-SUCCESS 'No lleva lrc= rc para no sobreescribir unSQL-SUCCESS-WITH-INFO CSQL. SQLTipoCol (I) = larg2 Case SQL-SUCCESS-WITH-INFO MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarError) CadError = CadError & MsgActual lrc = rc CSQL. SQLTipoCol (I) = larg2 Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarError) CadError = CadError & MsgActual lrc = rc HacerConsultaSQL = 1rc Exit Function End Select 'Determina las columnas que aceptan nulos sValor = String$ (BufferRet, O) iValor = O rc = SQLColAttributes(CSQL.ID, I, SQL- COLUMN-NULLABLE, SValor, 255, ivalor, larg2) Select Case rc Case SQL-SUCCESS 'NO lleva lrc = rc para no sobreescribir un SQL-SUCCESS-WITH-INFO CSQL.AceptaNuloCo1(I) = larg2 Case SQL-SUCCESS-WITH-INFO MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL-ID, MostrarError) CadError = CadError & MsgActual lrc = rc CSQL .AceptaNuloCol ( I ) = larg2 Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarError) CadError = CadError & MsgActual lrc = rc HacerConsultaSQL = lrc Exit Function End Select Next I 'Prepara los subparámetros de registro de la Consulta CSQL.Registro.NumRegAct = 0 ReDirn CSQL.Registro.DatosCol(1 To TotalCols) AS String HacerConsultaSQL = lrc 145 End Function Public Function HacerEscrituraSQL(BD As BaseDatos, ByVal SQL$, CSQL As ConsultaSQL, ByVal MostrarError, CadErrorS) As Integer Dim Cr As String Cr = Chr (13) & Chr (10) Dim lrc As Integer Dim MsgActual As String 'Utilizar SQLPrepare, SQLBindParameter, SQLExec, SQLRowCount. 'Si se repite regresar a SQLExec y por último SQLTransac lrc = SQL-SUCCESS CadError = MsgActual = " " 'Inicializa el handlede instrucción para cada operación de escritura If CSQL.ID <> SQL NULL-HSTMT Then 'Cierra la consulta actual pero no libera el handle rc = SQLFreeStmt(CSQL.ID, SQL CLOSE) 'Libera el handlede instrucción y sus recursos ID, rc = SQLFreeStmt (CSQL. SQL-DROP) de instrucción CSQL.ID = SQL-NULL-HSTMT 'Asigna un valor nulo al handle End If 'Obtiene un nuevo handle de instrucción rc = SQLAllocStmt (BD. ID, CSQL. ID) 'Prueba los casos en los que puede caerrc Select Case rc Case SQL-SUCCESS lrc = rc Case SQL SUCCESS-WITH-INFO CadError = DescErrorODBC (SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarError) lrc = rc Case SQL-ERROR CadError = DescErrorODBC(SQL_NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarError) lrc = rc HacerEscrituraSQL = lrc Exit Function End Select "I' CSQL.InstSQL = SQL I********************************************* 'Remite el Query rc = SQLExecDirect (CSQL. I D , SQL, Len(SQL)) Select Case rc Case SQL SUCCESS 'No lleva lrc= rc para no sobreescribir un SQL-SUCCESS-WITH-INFO Case SQL-SUCCESS-WITH-INFO MsgActual = DescErrorODBC(SQL_NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc Case SQL-NEED-DATA lrc = rc HacerEscrituraSQL = lrc Exit Function Case SQL-ERROR 146 MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL- NULL-HDBC, CSQL.ID, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc HacerEscrituraSQL = Irc Exit Function End Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 'Obtiene el número de parámetros implicados CSQL.NumCols = 0 rc = SQLNumParams(CSQL.ID, CSQL.NumCols) Select Case rc Case SQL-SUCCESS 'No lleva lrc = rc para no sobreescribir Case SQL-SUCCESS-WITH-INFO MsgActual = DescErrorODBC(SQL-NULL-HENV, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL-HENV, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc HacerEscrituraSQL = lrc Exit Function End Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . en la operación de escritura un SQL-SUCCESS-WITH-INFO SQL- NULL-HDBC, CSQL.ID, - SQL-NULL-HDBC, CSQL.ID, - . . . . . . . . . . . . 'Obtiene el número de renglones afectados por la escritura CSQL.NumRegs = O rc = SQLRowCount(CSQL.ID, CSQL.NumRegs) Select Case rc Case SQL-SUCCESS 'No lleva lrc = rc para no sobreescribir un SQL-SUCCESS-WITH-INFO Case SQL-SUCCESS-WITH-INFO MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc HacerEscrituraSQL = lrc Exit Function End Select 'prepara los subparámetros de registro de la consulta CSQL.Registro.NumRegAct = 0 ReDim cSQL.Registro.DatosCol(1 To CSQL.NumCols) As String HacerEscrituraSQL = End Function Dim Cr As String 1rc 147 Cr = Chr(l3) & Chr(l0) Dim lrc As Integer Dim MsgActual As String lrc = SQL-SUCCESS CadError = " " MsgActual = 'Inicializa el handle de instrucción para cada operación de escritura If CSQL.ID <> SQL NULL HSTMT Then 'Cierra la consulta-actual pero no libera el handle rc = SQLFreeStmt(CSQL.ID, SQL CLOSE) 'Libera el handlede instrucción y sus recursos rc = SQLFreeStmt (CSQL. SQL-DROP) ID, CSQL.ID = SQL-NULL-HSTMT 'Asigna un valor nuloal handle de instrucción End If 'Obtiene un nuevo handlede instrucción rc = SQLAllocStmt(BD.ID, CSQL.ID) 'Prueba los casosen los que puede caerrc Select Case rc Case SQL -SUCCESS lrc = rc Case SQL SUCCESS-WITH-INFO CadError = DescErrorODBC (SQL-NULL-HENV, SQL- NULL - HDBC, CSQL. - ID, MostrarError) lrc = rc Case SQL-ERROR CadError = DescErrorODBC (SQL -NULL-HENV, SQL-NULL- HDBC,CSQL.ID, MostrarError) lrc = rc HacerEscDirectaSQL = lrc Exit Function End Select I"' CSQL.InstSQL . . . . . . . . . . . . . = SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 'Remite el Query rc = SQLExecDirect (CSQL.ID, SQL, Len(SQL)) Select Case rc Case SQL-SUCCESS 'No lleva lrc = rc para no sobreescribir unSQL-SUCCESS-WITH-INFO Case SQL SUCCESS-WITH-INFO MsgActual = DescErrorODBC (SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc Case SQLNEED-DATA lrc = rc HacerEscDirectaSQL = lrc Exit Function Case SQL-ERROR MsgActual = DescErrorODBC(SQL_NULL-HENV, SQL-NULL- HDBC, CSQL.ID,MostrarError) CadError = CadError & Cr & MsgActual lrc = rc HacerEscDirectaSQL = lrc Exit Function End Select ........................................................................ 'Obtiene el número de parámetros implicados enla operación de escritura CSQL.NumCols = O rc = SQLNumParams(CSQL.ID, CSQL.NumCols) Select Case rc Case SQL-SUCCESS 'No lleva lrc= rc para no sobreescribir un SQL-SUCCESS-WITH-INFO Case SQL-SUCCESS-WITH-INFO MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc HacerEscDirectaSQL = lrc Exit Function End Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 'Obtiene el nímerode renglones afectados por la escritura CSQL.NumRegs = O rc = SQLRowCount (CSQL. ID, CSQL.NumRegs Select Case rc Case SQL SUCCESS 'No lleva lrc= rc para no sobreescribir unSQL-SUCCESS-WITH-INFO Case SQL-SUCCESS-WITH-INFO MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarError) CadError = CadError & Cr & MsgActual lrc = rc HacerEscDirectaSQL = Irc Exit Function End Select 'Prepara los subparámetros de registro de la consulta CSQL.Registro.NumRegAct= 0 ReDim CSQL.Registro.DatosCol(CSQL.NumCo1s) As String HacerEscDirectaSQL = lrc End Function Public Function PriRegConsultaSQL(CSQL As ConsultaSQL, ByVal MostrarErr, CadErrS) As Integer Dim Dim Dim Dim Dim Dim I As Integer * BufferRet sValor As String lValor As Long DatoCol As String MsgActual As String lrc As Integer CadErr = "I' . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 'Regresa al iniciode el registro rc = RepUltimaConsulta (CSQL, MostrarErr, Select Case rc CadErr) 149 Case SQL-SUCCESS lrc = rc Case SQL-SUCCESS-WITH-INFO lrc = rc Case SQL-ERROR lrc = rc PriRegConsultaSQL = lrc Exit Function Case SQL INVALID HANDLE PriReTConsultaSQL = SQL-ERROR Exit Function End Select 'Obtiene un renglón de el resultado del Query ID) rc = SQLFetch (CSQL. 'Prueba todoslos posibles resultadosde rc Select Case rc Case SQL-SUCCESS lrc = rc Case SQL-SUCCESS-WITH-INFO MsgActual = DescErrorODBC(SQL_NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarErr) CadErr = CadErr & MsgActual lrc = rc Case SQL-NO-DATA-FOUND lrc = rc PriRegConsultaSQL = lrc Exit Function Case SQL-INVALID HANDLE MsgActual = "Handle inválido" CadErr = CadErr & Cr & MsgActual lrc = rc PriRegConsultaSQL = lrc Exit Function Case SQL- STILL-EXECUTING lrc = rc PriRegConsultaSQL = lrc Exit Function Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL - HENV, SQL-NULL-HDBC, CSQL.ID, MostrarErr) CadErr = CadErr & MsgActual lrc = rc PriRegConsultaSQL = lrc Exit Function End Select I f rc = SQL-SUCCESS Or rc = SQL-SUCCESS-WITH -INFO Then CSQL.Registro.NumRegAct = 1 'Asignar un dato a cada elementode RSQL For I = 1 To CSQL.NumCols sValor = String$(BufferRet, O) lValor = O rc = SQLGetData (CSQL.ID, I, SQL-C-CHAR, sValor, BufferRet, 1Valor) Select Case rc Case SQL-SUCCESS Case SQL-SUCCESS-WITH-INFO MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarErr) CadErr = CadErr & MsgActual lrc = rc Case SQL-NO-DATA-FOUND lrc = rc PriRegConsultaSQL = lrc 150 Exit Function Case SQL-STILL-EXECUTING lrc = rc PriRegConsultaSQL = lrc Exit Function Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarErr) CadErr = CadErr & MsgActual lrc = rc PriRegConsultaSQL = lrc Exit Function End Select 'Verificar datos nulos If lValor < 1 Then 'Dato con valor nulo DatoCol = valor algún con 'Dato Else DatoCol = Left$ (sValor, 1Valor) End If CSQL.Registro.DatosCol(1)= DatoCol Next I End If "I' End Function Private Function RepUltimaConsulta(CSQL As ConsultaSQL, ByVal MostrarErr, CadErr$) As Integer Dim Dim Dim Cr = lrc As Integer MsgActual As String Cr As String Chr(l3) & Chr(l0) lrc = SQL-SUCCESS CadErr = " " MsgActual = " " . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 'Inicializa el handle de instrucción perono lo libera 'Cierra la consulta actual pero no libera el handle rc = SQLFreeStmt(CSQL.ID, SQL-CLOSE) Select Case rc Case SQL-SUCCESS lrc = rc Case SQL SUCCESS-WITH-INFO CadErr = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MOstrarErr) lrc = rc Case SQL-ERROR CadErr = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarErr) lrc = rc RepUltimaConsulta = lrc Exit Function Case SQL INVALID-HANDLE CadErr = "Handle inválido" RepUltimaConsulta = SQL-ERROR Exit Function End Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 'Ajusta las opcionesde el handle de instrucción rc = SQLSetStmtOption(CSQL.ID, SQL- CURSOR-TYPE, SQL- CURSOR-STATIC) Select Case rc Case SQL-SUCCESS 'NO lleva lrc = rc para no sobreescribir un SQL-SUCCESSWITH-INFO 151 Case SQL SUCCESS-WITH-INFO MsgActual = DescErrorODBC (SQL-NULL-HENV, SQL- NULL -HDBC, CSQL.ID, MostrarErr) CadErr = CadErr & Cr & MsgActual lrc = rc Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarErr) CadErr = CadErr & Cr & MsgActual lrc = rc RepUltimaConsulta = lrc Exit Function End Select rc = SQLSetStmtOption(CSQL.ID, SQL-ROWSET-SIZE, MAX-ROWS) Select Case rc Case SQL-SUCCESS 'No lleva lrc= rc para no sobreescribir un SQL-SUCCESS-WITH-INFO Case SQL SUCCESS WITH INFO MsgAccual = DgscErrorODBC (SQL-NULL-HENV, SQL- NULL -HDBC, CSQL.ID, MostrarErr) CadErr = CadErr & Cr & MsgActual lrc = rc Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarErr) CadErr = CadErr & Cr & MsgActual lrc = rc RepUltimaConsulta = lrc Exit Function End Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 'Se remite el mismo query otra ves. Esta es una forma de mover 'al principioal cursor rc = SQLExecDirect(CSQL.ID, CSQL.InstSQL, Len(CSQL.1nstSQL)) Select Case rc Case SQL-SUCCESS 'No lleva lrc = rc para no sobreescribir un SQL-SUCCESS-WITH-INFO Case SQL SUCCESS WITH INFO MsgAccual = DescErrorODBC (SQL-NULL-HENV, SQL- NULL ID, - HDBC, CSQL. MostrarErr) CadErr = CadErr & Cr & MsgActual lrc = rc Case SQL-NEED-DATA lrc = rc RepUltimaConsulta = lrc Exit Function Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL- NULL-HDBC, CSQL.ID, MostrarErr) CadErr = CadErr & Cr & MsgActual lrc = rc RepUltimaConsulta = lrc Exit Function End Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 'Reinicializa los valores de registro de la consulta CSQL.Registro.NumRegAct = O RepUltimaConsulta End Function = lrc 152 Public Function SigRegConsultaSQL(CSQL As ConsultaSQL, ByVal MostrarErr, CadErr$) As Integer Dim Cr As String Cr = Chr(l3) & Chr(l0) Dim I As Integer Dim sValor As String * BufferRet Dim lValor As Long Dim DatoCol As String Dim MsgActual As String Dim lrc As Integer CadErr = 'Obtiene un renglón de el resultado del Query rc = SQLFetch (CSQL. ID) 'Prueba todos l o s posibles resultados de rc Select Case rc Case SQL-SUCCESS lrc = rc Case SQL-SUCCESS-WITH INFO MsgActual = DescErgorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarErr) CadErr = CadErr & Cr & MsgActual lrc = rc Case SQL-NO-DATA-FOUND Irc = rc SigRegConsultaSQL = lrc Exit Function Case SQL INVALID-HANDLE MsgAccual = "Handle inválido" CadErr = CadErr & Cr.& MsgActual lrc = rc SigRegConsultaSQL = lrc Exit Function Case SQL -STILL-EXECUTING lrc = rc SigRegConsultaSQL = lrc Exit Function Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL- HDBC, CSQL.ID,MostrarErr) CadErr = CadErr & Cr & MsgActual lrc = rc SigRegConsultaSQL = lrc Exit Function End Select If rc = SQL-SUCCESS Or rc = SQL-SUCCESS-WITH-INFO Then CSQL.Registro.NumRegAct = CSQL.Registro.NumRegAct + 1 'Asignar un dato a cada elemento de RSQL For I = 1 To CSQL.NumCols O) sValor = String$ (BufferRet, lvalor = O rc = SQLGetData(CSQL.ID, I, SQL-C-CHAR, ?,Valor, BufferRet, 1Valor) Select Case rc Case SQL-SUCCESS Case SQL SUCCESS-WITH-INFO MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarErr) CadErr = CadErr & Cr & MsgActual lrc = rc Case SQL-NO-DATA-FOUND lrc = rc I"' 153 SigRegConsultaSQL = lrc Exit Function Case SQL-STILL-EXECUTING lrc = rc SigRegConsultaSQL = lrc Exit Function Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL_HENV, MostrarErr) CadErr = CadErr & Cr & MsgActual lrc = rc SigRegConsultaSQL = lrc Exit Function End Select 'Verificar datosnulos If lValor < 1 Then 'Dato con valor nulo DatoCol = " " valor algún con 'Dato Else DatoCol = Left$ (sValor, 1Valor) End If CSQL.Registro.DatosCol(1)= DatoCol Next I End If SQL-NULL-HDBC, CSQL.ID, - End Function Public Function UltRegConsultaSQL(CSQL As ConsultaSQL, ByVal MostrarErr, CadErrS) As Integer Dim Cr As String Cr = Chr(l3) & Chr(l0) Dim I As Integer Dim sValor As String* BufferRet Dim lValor As Long Dim DatoCol As String Dim MsgActual As String Dim lrc As Integer lrc = SQL-SUCCESS CadErr = " " 'Prueba si ya está en el último registro. De ser así simplemente sale If CSQL.Registro.NumRegAct= CSQL.NumRegs Then UltRegConsultaSQL = SQL-SUCCESS Exit Function End If 'Realiza el Fetch hasta llegar al último registro Do rc = SQLFetch (CSQL. ID) Select Case rc Case SQL-SUCCESS 'No lleva Irc = rc para no sobreescribir un SQL-SUCCESS-WITH-INFO CSQL.Registro.NumRegAct = CSQL.Registro.NumRegAct + 1 Case SQL SUCCESS WITH INFO CSQL. Registro rNumR<gAct = CSQL. Registro. NumRegAct + 1 MsgActual = DescErrorODBC(SQL_NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarErr) CadErr = CadErr & Cr & MsgActual lrc = rc Case SQL-INVALID-HANDLE MsgActual = "Handle Inválido" CadErr = CadErr & Cr & MsgActual lrc = rc 154 UltRegConsultaSQL = rc Exit Function Case SQL-NO-DATA-FOUND MsgActual = "NO haymás datos" CadErr = CadErr & Cr & MsgActual lrc = rc UltRegConsultaSQL = lrc Exit Function Case SQL-STILL-EXECUTING lrc = rc UltRegConsultaSQL = lrc Exit Function Case SQL-ERROR MsgActual = DescErrorODBC(SQL_NULL-HENV, SQL-NULL-HDBCr CSQL.ID, MostrarErr) CadErr = CadErr & Cr & MsgActual lrc = rc UltRegConsultaSQL = lrc Exit Function End Select 'If CSQL.Registro.NumRegActMod MAX-DOEVS = O Then DoEvents = CSQL.NumRegs Loop Until CSQL.Registro.NumRegAct 'Asignar un dato a cada elemento de RSQL For I = 1 To CSQL.NumCols o) sValor = String$ (BufferRet, lvalor = O rc = SQLGetData (CSQL. ID, I, SQL-C-CHAR, sValor, BufferRet, 1Valor) Select Case rc Case SQL-SUCCESS Case SQL-SUCCESS-WITH-INFO MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.IDr MostrarErr) CadErr = CadErr & MsgActual lrc = rc Case SQL -NO-DATA-FOUND lrc = rc UltRegConsultaSQL = lrc Exit Function Case SQL -STILL-EXECUTING lrc = rc UltRegConsultaSQL = lrc Exit Function Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBCr CSQL.ID, MostrarErr) CadErr = CadErr & MsgActual lrc = rc UltRegConsultaSQL = lrc Exit Function End Select 'Verificar datos nulos If lValor < 1 Then 'Dato con valor nulo DatoCol = " " 'Dato Else con valor algún DatoCol = Left$ (sValor, 1Valor) End I f CSQL.Registro.DatosCol(1) = DatoCol Next I UltRegConsultaSQL = lrc End Function 155 Public Function AntRegConsultaSQL(CSQL As ConsultaSQL, ByVal MostrarErr, CadErrS) As Integer Dim Cr As String Cr = Chr(l3) & Chr(l0) Dim RegAct As Long Dim I As Integer Dim sValor As String * BufferRet Dim lValor As Long Dim DatoCol As String Dim MsgActual As String Dim lrc As Integer lrc = SQL-SUCCESS CadErr = 'Guarda el número de el registro actual RegAct = CSQL.Registro.NumRegAct 'Si está en el primer registro simplemente sale sin hacer nada I f RegAct <= 1 Then AntRegConsultaSQL = lrc Exit Function End If "I' I************************************ 'Regresa al inicj.0 de el registro rc = RepUltimaConsulta (CSQL, MostrarErr, CadErr) Select Case rc Case SQL-SUCCESS lrc = rc Case SQL-SUCCESS-WITH-INFO lrc = rc Case SQL-ERROR lrc = rc AntRegConsultaSQL = lrc Exit Function Case SQL-INVALID-HANDLE AntRegConsultaSQL = SQL-ERROR Exit Function End Select 'Hace el Fetch hasta encontrar el registro anterioral actual Do 'Obtiene un renglbn de el resultado del Query rc = SQLFetch (CSQL. ID) 'Prueba todos l o s posibles resultados derc Select Case rc Case SQL-SUCCESS CSQL.Registro.NumRegAct = CSQL.Registro.NumRegAct + 1 lrc = rc Case SQL SUCCESS WITH-INFO CSQL.FegistroFNumRegAct = CSQL.Registro.NumRegAct + 1 MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarErr) CadErr = CadErr & Cr & MsgActual lrc = rc Case SQL-NO-DATA -FOUND lrc = rc AntRegConsultaSQL = lrc Exit Function Case SQL INVALID-HANDLE MsgAct%al = "Handle inválido" CadErr = CadErr & Cr & MsgActual lrc = rc 156 AntRegConsultaSQL = 1rc Exit Function Case SQL-STILL-EXECUTING lrc = rc AntRegConsultaSQL = lrc Exit Function Case SQL-ERROR MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQL.ID, MostrarErr) CadErr = CadErr & Cr & MsgActual lrc = rc AntRegConsultaSQL = lrc Exit Function End Select Loop Until CSQL.Registro.NumRegAct = RegAct - 1 If rc = SQL-SUCCESS Or rc = SQL-SUCCESS-WITH-INFO Then ‘Asignar un dato a cada elemento de RSQL For I = 1 To CSQL.NumCo1s O) sValor = String$ (BufferRet, lValor = O rc = SQLGetData(CSQL.ID, I, SQL-C-CHAR, SValor, BufferRet, 1Valor) Select Case rc Case SQL-SUCCESS Case SQL-SUCCESS-WITH-INFO MsgActual = DescErrorODBC(SQL-NULL-HENV, SQL-NULL-HDBC, CSQLvIDr MostrarErr) CadErr = CadErr & Cr & MsgActual lrc = rc Case SQL-NO-DATA-FOUND lrc = rc AntRegConsultaSQL = lrc Exit Function Case SQL-STILL-EXECUTING lrc = rc AntRegConsultaSQL = 1rc Exit Function Case SQL-ERROR MsgActual = DescErrorODBC(SQLNULL-HENV, SQL-NULL- HDBC,CSQL-ID, MostrarErr) CadErr = CadErr & Cr & MsgActual lrc = rc AntRegConsultaSQL = lrc Exit Function End Select ‘Verificar datos nulos If lValor < 1 Then ‘Dato con valor nulo DatoCol = Else ‘Dato valor algún con DatoCol = Left$ (sValor, 1Valor) End If CSQL.Registro.DatosCol(1) = DatoCol Next I End If AntRegConsultaSQL = lrc I”’ End Function Bibliografía Whiting, Bill. Teach yourself ODBC in 21 days. SAMS, 1996. Gryphon, Robert. Using ODBC 2. Que. 1995. Geiger, Kyle. Inside ODBC. Microsoft. 1995. Microsoft Publishing. Programer's reference: Microsoft open database connectivity Software development kit, version 2.0 for the Microsoft Windows and Windows NT operating systems. Microsoft, 1994. Reese, George. Database programming with JDBC and Java. O'Reilly, 1997. Hobbs, Ashton. Teach yourself database programming wifh JDBC in 27 days. SAMS, 1997. 157 lndice tratamiento deerrores, 8 1-82 BringWindowToTop, WinAPI, 12 A Administrador de controladores deODBC. Véase ODBC, Driver Manager Administrador dehentes de datos deODBC, 3, 6 AntRegConsultaSQL,VB A P I , 71-72 A P I de JDBC contra otras APIs, 88 descripción, 85 ejemplo de uso, 102-3 ejemplo de uso, completo,106-7 API de ODBC aproximaciones deuso de la, 11 caracteristicas, 3 codificación directa, 1 1, 12 extensiones ala, 9 modelo de programación, 10, 12 niveles de conformidad, 51, 52 plataformas, 11 Apuntador de cadena, pasar un, 38 Apuntadores en Visual Basic, 37 C Cadena de conexión, 67 Cadena terminadaen nulo, 38 CadErrores, argumento de salida VB A P I , 66, 74 CallableStatement,JDBC API, 92 Caracter de escape ( \ ), 103 CerrarInstrucSQL. VB API, 72-73 CLASSPATH, 104,105, 106 Códigos de retorno,ODBC API, 26,3 1 Códigos de retorno,VB A P I , 6 1 Compilar un programa de Java, 107 ConectarBD, VB M I , 65-66 ConectarBD2, VB API, 66-67 Conectividad a bases de datos abiertas. Véase ODBC, definición de Conexion.bas, módulo de Visual Basic, 65 Conexiones abiertas, 21 Conexiones ODBC múltiples condiciones, 26 ejemplo, 25 Connection, JDBC API, 92, 100 ConsultaSQL, tipode dato abstracto, 61 Correr un programa compilado en Java, 108 B Base of code, 14 BaseDatos, tipo de dato abstracto, 60 Bases de datos cruzadas, 5 Beep, WinAF'I, 12 Biblioteca de funciones de Visual Basic definiciones de constantes, 61 ejemplo de uso, 77-79 formato de aplicaciones,80 funciones denavegación, 71-72 funciones ODBC involucradas, 62-63 funciones privadas,74-75 limitaciones dela, 60 origen de la, 59 tipos de datos propios, 60-61 D Data Source Name (DSN). Véase Nombre de fuente de datos DataBaseMetaData,JDBC M I , 92 DataTruncation, JDBC M I , 94 Date, JDBC A P I , 93 Declare, instrucción de VB, 12 DescErrorODBC, función privada VB API, 75 DesconectarBD, VB API,73-74 159 Driver Manager, ODBC API. Véase ODBC, Driver Manager Driver, JDBC A P I , 92,95 DriverManager, JDBC A P I , 93, 96 DriverPropertyInfo,JDBC A P I , 93 Drivers, deODBC, 5 Dynamic link library, 14 E EjecutarSQL, VB API, 70-7 1 ERROR-FUNC, 61,66,67,69,70,71,72,75 ErroresNativos, VBM I , 63,74 EXITO-FUNC, 61,66,67,68, 69, 70, 71, 72, 75,81 EXITO-FUNC-CON-INFO, 61,66,67,68,69,70,71, 72,75 EXPORT TABLE. 14 F Fuentes de datos dar dealta, configurar, 6-7 registrar dentro del código, 7 Funciones dela WinAPI, 38, 39 Funciones deODBC códigos de retorno,26, 3 1 de acuerdo a las instrucciones SQL,50 de conexión, 18 de liberación de recursos,21 del nivel 1, 54 del nivel 2, 55-56 del núcleo, 52-53 lineamientos de orden de llamada, 23-24 G Generar la clase de Java, 107 GlobalAlloc, WinAPI, 38, 39,40,41 GlobalLock, WinAPI, 38,39,40,41 H HacerConsultaSQL, VB API, 68-69 HacerEscDirectaSQL,VB MI,69-70 HacerEscrituraSQL, VBA P I , 70 Handle(s) de ambiente, 15, 16 de conexión, 16 de sentencia, 18, 22,25,33, 34 HANDLE-INVALIDO, 61,69,70,72,75 HDBC, 3 1 HENV, 3 1 Herramientas deJDK, 103, 104, 105 HSTMT, 3 1 I Información del error, ODBC API, 29 Instrucción SQL dinhica, I O0 Instrucci6n SQL estfitica, 1O0 Interfaz dealto nivel, 87 Interfaz de bajo nivel, 86 Interfaz de nivel de llamada, 9 Interfaz deProgramación de Aplicaciones,3 Interoperabilidad, 4 J Java Development Kit (JDK), 91, 103 Java, intérprete del jdk,105-6 Javac, compiladordel jdk, 104-5 JavaSoft, 86,87, 109 JDBC arquitectura de, 88 contra ODBC, 87-88 definición de, 85 descripción, 85 en Internet, 109 excepciones de, 94 funcionalidad, 86 interfaces de, 92-93 interfaces de alto nivel basadas en, 87 objetos de, 93-94 jdbcdrivers, propiedad, 96, 98 Jet database engine, 11 L Liberar recursos de conexión, 21 Librería de enlacedinhico, 14, 15,60 Lstrcpy, WinAPI, 41 Lstrcpyn, WinAPI, 38,39,41 M Manejo de cadenas de Visual Basic, 38 Manejo de errores en la VB API, 81-82 Manejo simplede errores, 27 Mensajes de error componentes de, 30 formatos de SQLError, 30 identidad del componente, 30 recuperar, 29 Método acceptsURL, interfaz Driver, 95 close, interfaz Connection, 100 close, interfaz Statement, 100 connect, conexión con, 95-96 connect, interfaz Driver, 95 createstatement, interfaz Connection,100 executeQuery, interfaz Statement, 101 executeupdate, interfaz Statement, 1O 1 forName, objetoClass, 96 getconnection, conexión con,97-98 getConnection, objeto DriverManager, 97 getFloat, objeto Resultset, 101 getproperties, objetoSystem, 96 getResuItSet, interfaz Statement,101 161 getstring, objeto Resultset, 101 next, objeto Resultset, 101 setproperties, objeto System, 96 MCtodos de la interfaz Driver, 95 del objeto DriverManager, 97 Middle-tier, modelo de acceso JDBC, 90 Modelo de codificación directa, arquitectura, 13 Modelo de programación, ODBC API, 10 MostrarErr, argumento de entrada VB API, 66, 75 Múltiples operaciones, 17-1 8 N NECESITA-DATOS, 61 NO-HAY-MAS-DATOS, 01,72 Nombre de fuente de datos, 18 Números de error, 74, 79,81 O Objetos de acceso a datos (DAO), 1 I Objetos de datos remotos (RDO),1 1 ODBC controladores de, 4 definición de, 3 Desktop Database Drivers, 5 , 6 Driver Manager, 5 en Internet, 56 funciones de nivel 1, 54 funciones de nivel 2, 55-56 núcleo de funciones, 5 1 , 52-53 porqué usar, 4 requisitos de conexión, 18 subprotocolo de. Véuse Subprotocolo ODBC en URLs de JDBC usar desde Java, 87-88 ODBC.IN1, 18 Odbc32.dl1, 5, 11, 14, 15, 37 Open Database Connectivity. Véuse ODBC, definición de Open Group Standard Structured Query Language, 4 P Página Web de JavaSoft, 109 Páginas Web sobre ODBC, 56 Paquete java.sq1, 91 Plataformas, ODBC A P I , 11 Preparedstatement, JDBC API, 92 PriRegConsultaSQL, VB A P I , 71-72 Protocolos en URLs, 98 Puentes JDBC-ODBC, 87 Q Quickview, I4 R Recuperar columnas individuales, 46 Redireccionable, 14 RegisterDatabase, 7 RegistroSQL, tipo de dato abstracto, 60 RepUltimaConsulta, función privada VB API, 75 Requisitos conexión ODBC, 18 Resultset, JDBC API, 92, 101 ResultSetMetaData, JDBC API, 93, 102 RETCODE, 3 1 S SDK ODBC, 5 SigRegConsultaSQL, VB API. 71-72 SIGUE-EJECUTANDO, 61 SQL Access Group, 3,29 SQL-NTS, 20,26,27,35,36 SQLAllocConnect, ODBC API, I7 SQLAllocEnv, ODBC API, 15-16 SQLAllocStmt, ODBC API, 33,34 SQLBindCol, ODBC API, 41-43 con cadenas de Visual Basic, 37,38-39 con tipos numkricos, 38 diferencia con SQLGetData, 46 función, 37 truncamiento de datos, 45 y la WinAPI, 39 SQLBrowseConnect, ODBC API, 19,55 SQLConnect, ODBC API, 19-20 SQLCHAR, 3 1 SQLDisconnect, ODBC API, 21-22 SQLDOUBLE, 3 1 SQLDriverConnect, ODBC API, 18, 19, 54, 63 SQLError, ODBC API, 27-29 SQLException, JDBC API, 94 SQLExecDirect, ODBC API, 35-36 SQLFetch, ODBC API, 44-45 SQLFreeConnect, ODBC API, 22-23 SQLFreeEnv, ODBC API, 23 SQLFreeStmt, ODBC API, 24 SQLGetData, ODBC M I , 4 7 4 9 recuperar columnas, 46 truncamiento de datos, 48 SQLGetFunctions, ODBC API, 19, 54 SQLGetInfo, ODBC API, 17, 18, 54 SQLHDBC, 3 I SQLHENV, 3 1 SQLHSTMT, 3 1 SQLINTEGER, 3 1 SQLNumResultCols, ODBC A P I , 43,46, 53,63 SQLPOINTER, 3 1 SQLREAL, 3 1 SQLRETURN, 3 1 SQLSetConnectOption, ODBC A P I , 19, 54, 63 SQLSMALLINT, 3 1 SQLWarning, JDBC API, 94 Statement, JDBC API, 93, 100 Structured Query Language (SQL), 4,85 Subprotocolo ODBC en URLs de JDBC, 99 162 Sun Microsystems, 85, 91, 109 T T-Prueba, tabla deejemplo, 77 Tabla de exportaci6n, 14 Three-tier, modelo de acceso JDBC, 90 Time, JDBC API, 93 Timestamp, JDBC A P I , 94 Tipos de datos, ODBC API, 3 1-32 Tipos de instrucciones SQL, 35 Two-tier, modelo de acceso JDBC, 89 Types, JDBC API, 94 UltRegConsultaSQL, VB API, 71-72 URL de JDBC, descripcibn, 99 descripci6n de un, 98 protocolos en, 98 V Variables de programa, asociar con columnas,37 Variables String de Visual Basic,37 X X/Open and SQL AccessGroup Call Level Interface, U UAM Iztapalapa, 59 19,51 X/Open e ISO/IEC, 3