Sybase ASE 15.0.2 / 15.0.3 et la méthode GetSchema du pilote ADO.NET 2.0 (sp_oledb_columns)

Introduction

La procédure stockée sp_oledb_columns change entre les versions 15.0.2 et 15.0.3 de Sybase Adaptive Server Enterprise. Ces modifications ont des impacts sur la méthode ADO.NET GetSchema de la classe ASEConnection du pilote ADO.NET 2.0 de Sybase (Sybase.AdoNet2.AseClient.dll).

Cet article passe en revue les modifications dans la procédure stockée sp_oledb_columns entre les versions 15.0.2 et 15.0.3 et les impacts potentiels sur les applications existantes développées en C# et utilisant la méthode GetSchema pour effectuer du "reverse" des objets (tables, procédures etc...). Si l'application C# exploite les résultats de GetSchema dans un environnement de développement ASE 15.0.3, celle-ci aura un comportent inattendu si la production est encore en version ASE 15.0.2.

La méthode GetSchema de la classe AseConnection du driver ADO.NET 2.0 de Sybase

Le driver ADO.NET 2.0 de Sybase

Le driver ADO.NET 2.0 de Sybase est disponible dans le SDK Open Server et Open Client Sybase 15.0 à partir de l'ESD #10. Pour plus d'informations : Open Server, Open Client, and SDK 15.0, New features in ESD # 10 (ADO.NET 2.0 support).

La documentation sur les APIs du driver ADO.NET 2.0 pour Sybase ASE est disponible dans la documentation Software Developer's Kit 15.5 : Software Developer's Kit 15.5,ADO.NET Data Provider 2.155 User's Guide.

Sybase.AdoNet2.AseClient.dll et sybdrvado20.dll sont les 2 DLLs vitales du driver ADO.NET 2.0 de Sybase ASE, DLL à embarquer dans la GAC ou l'IDE Visual Studio lors des développements.

La méthode GetSchema de la classe AseConnection

La méthode GetSchema de la classe AseConnection permet de faire du "reverse" avec C# des objets de bases de données afin de récupérer leurs caractéristiques et de les exploiter. Elle récupère les collections de méta-données (MetaData Collections), les collections les plus courantes sont :

  • Tables
  • Views
  • Columns
  • ViewColumns
  • Procedures
  • ProcedureParameters
  • Indexes
  • IndexColumns
  • DataTypes
  • Users

Voici un exemple de code simple afin de récupérer en C# les colonnes de la table SECURITIES_CHAR dans la base GSSA avec leurs caractéristiques grâce à la méthode GetSchema :

AseConnection connection = new AseConnection("Data Source=FRDGLO101;Port=30003;Initial Catalog=GSSA;User Id=sa;Password=******");

connection.Open();

DataTable SchemaTable = connection.GetSchema("Columns", new string[] { "SECURITIES_CHAR" });

le résultat est ensuite affiché dans la console, ligne par ligne, colonne par colonne :

foreach (DataRow row1 in SchemaTable.Rows)
{
  foreach (DataColumn col in SchemaTable.Columns)
  {
      Console.WriteLine(col.ToString() + " = " + row1[col].ToString());
  }
}

sp_oledb_columns, différences entre les versions 15.0.2 et 15.0.3

La procédure stockée sp_oledb_columns est installée dans la base sybsystemprocs par le script installmaster des distributions ASE ($SYBASE/$SYBASE_ASE/scripts) et elle est utilisée par les couches clientes OLE-DB et ADO.NET. sp_oledb_columns retourne au client les caractéristiques d'une ou des colonnes pour une table donnée.

À partir de la version 15.0.3, un nouveau paramètre @is_ado en 5è position est implémentée pour la procédure sp_oledbcolumns.

Prototype ASE 15.0.2 sp_oledb_columns Prototype ASE 15.0.3 sp_oledb_columns
CREATE PROCEDURE sp_oledb_columns (

@table_name varchar(771) = null,
@table_owner varchar(32) = null,
@table_qualifier varchar(32) = null,
@column_namevar char(771) = null )

AS
CREATE PROCEDURE sp_oledb_columns (

@table_name varchar(771) = null,
@table_owner varchar(32) = null,
@table_qualifier varchar(32) = null,
@column_name varchar(771) = null,
    @is_ado  int = 1 )

AS

Ce nouveau paramètre @is_ado de la version 15.0.3 est par défaut à 1 lorsqu'il n'est pas donné. Pour le moteur ASE, cela signifie que la couche ADO.NET cliente est la version 1 par défaut si le client ne lui notifie pas la version majeure ADO.NET.

Le nouveau paramètre @is_ado n'est pas anodin car il ne retourne pas les mêmes jeux de résultats pour @is_ado=1 et @is_ado=2. Il suffit de consulter le code source de la procédure sp_oledb_columns pour mieux le comprendre.

Pour la version 1, le type de la colonne est donné avec l'entête DATA_TYPE et l'identifiant du type (129 pour varchar).

Pour la version 2, le type de la colonne est donné avec l'entête TYPE_NAME et le nom explicite du type de la colonne (varchar).

sp_oledb_columns ASE 15.0.2 sp_oledb_columns ASE 15.0.3
@is_ado=1
sp_oledb_columns ASE 15.0.3
@is_ado=2
exec sp_oledb_columns
 @table_name = "SECURITIES_CHAR",
 @column_name = "SEDOL_CODE"
go

DATA_TYPE
---------
      129
exec sp_oledb_columns
 @table_name = "SECURITIES_CHAR",
 @column_name = "SEDOL_CODE",
 @is_ado = 1
go

DATA_TYPE
---------
      129
exec sp_oledb_columns
 @table_name = "SECURITIES_CHAR",
 @column_name = "SEDOL_CODE",
 @is_ado = 2
go

TYPE_NAME
---------
varchar

Le résultat des exemples ci-dessus est allegé pour la lisibilité.

L'identifiant 129 pour le type varchar provient de la table sybsystemprocs..spt_sybdrv :

select * from sybsystemprocs..spt_sybdrv where type_name='varchar'
go
 type_name            data_type   tds_type   
 -------------------- ----------- -----------
 varchar                      129          39

Interactions entre la méthode GetSchema du pilote ADO.NET 2 et sp_oledb_columns

La méthode GetSchema de la classe AseConnection s'appuie sur les procédures systèmes sp_oledb_% dans la base sybsystemprocs (sp_oledb_databases, sp_oledb_columns, sp_oledb_stored_procedures...) pour réaliser son "reverse engineering".

Pour les colonnes d'une table, sp_oledb_columns est appelée : une simple trace RIBO, un audit ou la consultation des tables MDA suffit à la détecter :

Appel GetSchema ADO.NET 2 Appel en base

DataTable SchemaTable = 
connection.GetSchema("Columns",
    new string[] { "SECURITIES_CHAR" });
exec sp_oledb_columns
@table_name="SECURITIES_CHAR",
@table_owner=null,
@table_qualifier=null,
@column_name=null,
@is_ado=2

Problématique et contournement

Dans le cas où l'application C# est développée et en recette dans des environnements ASE 15.0.3 et qu'elle exploite TYPE_NAME retourné par sp_oledb_columns, le problème peut se poser si cette application monte dans des environnements de production ASE encore en version 15.0.2, version pour laquelle sp_oledb_columns retourne un identifiant pour le type dans la colonne DATA_TYPE => crash et application non opérationnelle.

Sortir l'artillerie lourde consisterait à détecter en C# si la méthode GetSchema appelle sp_oledb_columns en version 15.0.2 ou 15.0.3 en réalisant les opérations ci-dessous :

  • Détection du nom de la colonne retournant le type de la colonne : DATA_TYPE ou TYPE_NAME.
  • Manipulation d'un tableau de transcodification : 129 équivaut à varchar etc...

Le temps que les environnements de production soient migrés vers la version 15.0.3, une solution très temporaire est envisageable, en voici les étapes :

  • Création de la procédure sp_oledb_columns_adonet2 dans la base sybsystemprocs des environnements ASE 15.0.2, procédure dont le code est celui de laversion 15.0.3.
  • Détection en C# de l'existence de la procédure stockée sp_oledb_columns_adonet2 dans la base sybsystemprocs avec GetSchema :
    connection.ChangeDatabase("sybsystemprocs");
    DataTable SchemaTable = connection.GetSchema("Procedures", new string[] { "sp_oledb_columns_adonet2" });
  • Si elle n'existe pas, exécution classique de la méthode GetSchema pour le "reverse".
  • Si elle existe, appel de la procédure stockée sp_oledb_columns_adonet2 avec les bons paramètres pour simuler ce que retournerait GetSchema en version 15.0.3.

Une recompilation de l'application est inévitable.

Lorsque l'intégralité du parc est migré en version 15.0.3 : supprimer la procédure sp_oledb_columns_adonet2 et le bout de code C# détectant l'existence de celle-ci.