Win32-SqlServer

 view release on metacpan or  search on metacpan

init.cpp  view on Meta::CPAN

 * User: Sommar       Date: 08-04-30   Time: 22:46
 * Updated in $/Perl/OlleDB
 * Use get_sv and not perl_get_sv (deprecated). Pass GV_ADDMULTI to get_sv
 * to avoid "Used only once" warning. Don't define macro XS_VERSION in the
 * file, as it comes with the Makefile.
 *
 * *****************  Version 2  *****************
 * User: Sommar       Date: 08-01-06   Time: 23:33
 * Updated in $/Perl/OlleDB
 * Replaced all unsafe CRT functions with their safe replacements in VC8.
 * olledb_message now takes a va_list as argument, so we pass it
 * parameterised strings and don't have to litter the rest of the code
 * with that.
 *
 * *****************  Version 1  *****************
 * User: Sommar       Date: 07-12-24   Time: 21:40
 * Created in $/Perl/OlleDB
  ---------------------------------------------------------------------*/

#define _WIN32_DCOM   // Needed for CoInitializeEx

#include "CommonInclude.h"

#include <cguid.h>
#include <msdaguid.h>


#include "convenience.h"
#include "datatypemap.h"
#include "init.h"



#undef FILEDEBUG
#ifdef FILEDEBUG
FILE *dbgfile = NULL;
#endif


// Global variables for class ids for the possible providers.
CLSID  clsid_sqloledb     = CLSID_NULL;
CLSID  clsid_sqlncli      = CLSID_NULL;
CLSID  clsid_sqlncli10    = CLSID_NULL;
CLSID  clsid_sqlncli11    = CLSID_NULL;
CLSID  clsid_msoledbsql   = CLSID_NULL;
CLSID  clsid_msoledbsql19 = CLSID_NULL;

// This global array holds definition of all initialisation properties
// for OLE DB.
init_property gbl_init_props[MAX_INIT_PROPERTIES];

// Number of properties per provider:
int no_of_ssprops_sqloledb     = -1;
int no_of_ssprops_sqlncli      = -1;
int no_of_ssprops_sqlncli10    = -1;
int no_of_ssprops_sqlncli11    = -1;
int no_of_ssprops_msoledbsql   = -1;
int no_of_ssprops_msoledbsql19 = -1;

// This array holds where each property set starts in gbl_init_props;
propset_info_struct init_propset_info[NO_OF_INIT_PROPSETS];


// Global pointer to OLE DB Services. Set once when we intialize, and
// never released.
IDataInitialize * data_init_ptr    = NULL;

// Global pointer the OLE DB conversion library.
IDataConvert    * data_convert_ptr = NULL;

// Global pointer to the IMalloc interface. Most of the time when we allocate
// memory, we rely on the Perl methods. However, there are situations when
// we must free memory allocated by SQLOLEDB. Same here, we create once, as
// the COM implementation is touted as thread-safe.
IMalloc*   OLE_malloc_ptr = NULL;



// A helper routine to get default for APPNAME.
static BSTR get_scriptname () {
   // Get the name of the script, taken from Perl var $0. This is used as
   // the default application name in SQL Server.

   SV* sv;

   if (sv = get_sv("0", FALSE))
   {
      // Get script name into a BSTR.
      BSTR tmp = SV_to_BSTR(sv);
      BSTR scriptname;
      WCHAR *p;

      // But this name is full path, and we want only the trailing bit.
      if (p = wcsrchr(tmp, '/'))
         ++p;
      else if (p = wcsrchr(tmp, '\\'))
         ++p;
      else if (p = wcsrchr(tmp, ':'))
          ++p;
      else
          p = tmp;

      scriptname = SysAllocString(p);
      SysFreeString(tmp);
      return scriptname;
   }
   else {
      return NULL;
   }
}

// And another one to get the default for WSID.
static BSTR get_hostname() {
   BSTR hostname = SysAllocStringLen(NULL, 31);
   memset(hostname, 0, 60);
   GetEnvironmentVariable(L"COMPUTERNAME", hostname, 30);
   return hostname;
}

// Add a property to the global array.
// isoptoinal: properties that were added in a .X version of a provider, or
// options where we want the provider default to apply.
static void add_init_property (const char *  name,
                               init_propsets propset_enum,
                               DBPROPID      propid,
                               BOOL          isoptional, 
                               VARTYPE       datatype,
                               BOOL          default_empty,
                               const WCHAR * default_str,
                               int           default_int,
                               int          &ix)
{

   // Check that we are not exceeding the global array. Note that the last
   // slot must be left unusued, as this is used as a stop condition!
   if (ix >= MAX_INIT_PROPERTIES - 1) {
      croak("Internal error: size of array for init properties exceeded");
   }

   // Increment property set counter.
   init_propset_info[propset_enum].no_of_props++;

   strcpy_s(gbl_init_props[ix].name, INIT_PROPNAME_LEN, name);
   gbl_init_props[ix].propset_enum  = propset_enum;
   gbl_init_props[ix].property_id   = propid;
   gbl_init_props[ix].isoptional    = isoptional;
   gbl_init_props[ix].datatype      = datatype;
   VariantInit(&gbl_init_props[ix].default_value);

   if (! default_empty) {
      gbl_init_props[ix].default_value.vt = datatype;

      switch (datatype) {
         case VT_BOOL :
            gbl_init_props[ix].default_value.boolVal = default_int;
            break;

         case VT_I2 :
            gbl_init_props[ix].default_value.iVal = default_int;
            break;

         case VT_UI2 :
            gbl_init_props[ix].default_value.uiVal = default_int;
            break;

         case VT_I4 :
            gbl_init_props[ix].default_value.lVal = default_int;
            break;

         case VT_BSTR :
               gbl_init_props[ix].default_value.bstrVal = SysAllocString(default_str);
            break;

         default :
            croak ("Internal error: add_init_property was called witn unhandled vartype %d",
                    datatype);
            break;
       }
    }

    // And increase the index.
    ix++;
}

// And this is the routine that sets up the array.
static void setup_init_properties ()
{
   int ix = 0;
   BSTR scriptname = get_scriptname();
   BSTR hostname   = get_hostname();

   // Init array so that all entrys are unused and init propset_info.
   memset(gbl_init_props, not_in_use,
          MAX_INIT_PROPERTIES * sizeof(init_property));


   // DBPROPSET_DBINIT, main OLE DB init and auth properties.
   init_propset_info[oleinit_props].start = ix;
   init_propset_info[oleinit_props].no_of_props = 0;

   add_init_property("IntegratedSecurity", oleinit_props, DBPROP_AUTH_INTEGRATED,
                     FALSE, VT_BSTR, FALSE, L"SSPI", NULL, ix);
   add_init_property("Password", oleinit_props, DBPROP_AUTH_PASSWORD,
                     FALSE, VT_BSTR, TRUE, NULL, NULL, ix);
   add_init_property("Username", oleinit_props, DBPROP_AUTH_USERID,
                     FALSE, VT_BSTR, TRUE, NULL, NULL, ix);
   add_init_property("Database", oleinit_props, DBPROP_INIT_CATALOG,
                     FALSE, VT_BSTR, FALSE, L"tempdb", NULL, ix);
   add_init_property("Server", oleinit_props, DBPROP_INIT_DATASOURCE,
                     FALSE, VT_BSTR, FALSE, L"(local)", NULL, ix);
   add_init_property("GeneralTimeout", oleinit_props, DBPROP_INIT_GENERALTIMEOUT,
                     FALSE, VT_I4, FALSE, NULL, 0, ix);
   add_init_property("LCID", oleinit_props, DBPROP_INIT_LCID,
                     FALSE, VT_I4, FALSE, NULL, GetUserDefaultLCID(), ix);
   add_init_property("Pooling", oleinit_props, DBPROP_INIT_OLEDBSERVICES,
                     FALSE, VT_I4, FALSE, NULL, DBPROPVAL_OS_RESOURCEPOOLING, ix);
   add_init_property("Prompt", oleinit_props, DBPROP_INIT_PROMPT,
                     FALSE, VT_I2, FALSE, NULL, DBPROMPT_NOPROMPT, ix);
   add_init_property("ConnectionString", oleinit_props, DBPROP_INIT_PROVIDERSTRING,
                     FALSE, VT_BSTR, TRUE, NULL, NULL, ix);
   add_init_property("ConnectTimeout", oleinit_props, DBPROP_INIT_TIMEOUT,
                     FALSE, VT_I4, FALSE, NULL, 15, ix);

   // DBPROPSET_SQLSERVERDBINIT, SQLOLEDB specific proprties.
   init_propset_info[ssinit_props].start = ix;
   init_propset_info[ssinit_props].no_of_props = 0;

   add_init_property("Appname", ssinit_props, SSPROP_INIT_APPNAME,
                     FALSE, VT_BSTR, FALSE, scriptname, NULL, ix);
   add_init_property("Autotranslate", ssinit_props, SSPROP_INIT_AUTOTRANSLATE,
                     FALSE, VT_BOOL, FALSE, NULL, NULL, ix);   // This property is not settable by the user, but we also force it as FALSE.
   add_init_property("Language", ssinit_props, SSPROP_INIT_CURRENTLANGUAGE,
                     FALSE, VT_BSTR, TRUE, NULL, NULL, ix);
   add_init_property("AttachFilename", ssinit_props, SSPROP_INIT_FILENAME,
                     FALSE, VT_BSTR, TRUE, NULL, NULL, ix);
   add_init_property("NetworkAddress", ssinit_props, SSPROP_INIT_NETWORKADDRESS,
                     FALSE, VT_BSTR, TRUE, NULL, NULL, ix);
   add_init_property("Netlib", ssinit_props, SSPROP_INIT_NETWORKLIBRARY,
                     FALSE, VT_BSTR, TRUE, NULL, NULL, ix);
   add_init_property("PacketSize", ssinit_props, SSPROP_INIT_PACKETSIZE,
                     FALSE, VT_I4, TRUE, NULL, NULL, ix);
   add_init_property("UseProcForPrep", ssinit_props, SSPROP_INIT_USEPROCFORPREP,
                     FALSE, VT_I4, FALSE, NULL, SSPROPVAL_USEPROCFORPREP_OFF, ix);
   add_init_property("Hostname", ssinit_props, SSPROP_INIT_WSID,
                     FALSE, VT_BSTR, FALSE, hostname, NULL, ix);
   add_init_property("Encrypt", ssinit_props, SSPROP_INIT_ENCRYPT,
                     TRUE, VT_BSTR, TRUE, NULL, NULL, ix);  // Set as is_optional, to permit for different provider defaults.
   // The above properties are those that are in SQLOLEDB.
   no_of_ssprops_sqloledb = init_propset_info[ssinit_props].no_of_props;

   // These properties were added in SQL 2005.
   add_init_property("FailoverPartner", ssinit_props, SSPROP_INIT_FAILOVERPARTNER,
                     FALSE, VT_BSTR, TRUE, NULL, NULL, ix);
   add_init_property("TrustServerCert", ssinit_props, SSPROP_INIT_TRUST_SERVER_CERTIFICATE,
                     FALSE, VT_BOOL, TRUE, NULL, NULL, ix);
   add_init_property("OldPassword", ssinit_props, SSPROP_AUTH_OLD_PASSWORD,
                     FALSE, VT_BSTR, TRUE, NULL, NULL, ix);
   no_of_ssprops_sqlncli = init_propset_info[ssinit_props].no_of_props;

   // These two were added with SQL 2008.
   add_init_property("ServerSPN", ssinit_props, SSPROP_INIT_SERVERSPN,
                     FALSE, VT_BSTR, TRUE, NULL, NULL, ix);
   add_init_property("FailoverPartnerSPN", ssinit_props, SSPROP_INIT_FAILOVERPARTNERSPN,
                     FALSE, VT_BSTR, TRUE, NULL, NULL, ix);
   no_of_ssprops_sqlncli10 = init_propset_info[ssinit_props].no_of_props;

   // And here is a single one that made it into SQL 2012.
   add_init_property("ApplicationIntent", ssinit_props, SSPROP_INIT_APPLICATIONINTENT,
                     FALSE, VT_BSTR, FALSE, L"ReadWrite", NULL, ix);
   no_of_ssprops_sqlncli11 = init_propset_info[ssinit_props].no_of_props;

   // This one appeared first with the undeprecated driver in 2018.
   add_init_property("MultiSubnetFailover", ssinit_props, SSPROP_INIT_MULTISUBNETFAILOVER,
                     TRUE, VT_BOOL, FALSE, NULL, FALSE, ix);
   add_init_property("AccessToken", ssinit_props, SSPROP_AUTH_ACCESS_TOKEN,
                     TRUE, VT_BSTR, TRUE, NULL, FALSE, ix);
   add_init_property("Authentication", ssinit_props, SSPROP_AUTH_MODE,
                     TRUE, VT_BSTR, TRUE, NULL, FALSE, ix);
   add_init_property("ConnectRetryCount", ssinit_props, SSPROP_INIT_CONNECT_RETRY_COUNT,
                     TRUE, VT_I4, FALSE, NULL, 1, ix);
   add_init_property("ConnectRetryInterval", ssinit_props, SSPROP_INIT_CONNECT_RETRY_INTERVAL,
                     TRUE, VT_I4, FALSE, NULL, 10, ix);
   add_init_property("TransparentNetworkIPResolution", ssinit_props, SSPROP_INIT_TNIR,
                     TRUE, VT_BOOL, FALSE, NULL, FALSE, ix);
   no_of_ssprops_msoledbsql = init_propset_info[ssinit_props].no_of_props;

   // Properties new in MSOLEDBSQL19.
   add_init_property("HostNameInCertificate", ssinit_props, SSPROP_INIT_HOST_NAME_CERTIFICATE,
                     FALSE, VT_BSTR, TRUE, NULL, FALSE, ix);
   add_init_property("ServerCertificate", ssinit_props, SSPROP_INIT_SERVER_CERTIFICATE,
                     FALSE, VT_BSTR, TRUE, NULL, FALSE, ix);
   no_of_ssprops_msoledbsql19 = init_propset_info[ssinit_props].no_of_props;   

   
   // DBPROPSET_DATASOURCE, data-source properties.
   init_propset_info[datasrc_props].start = ix;
   init_propset_info[datasrc_props].no_of_props = 0;

   add_init_property("MultiConnections", datasrc_props, DBPROP_MULTIPLECONNECTIONS,
                     FALSE, VT_BOOL, FALSE, NULL, FALSE, ix);

   SysFreeString(scriptname);
   SysFreeString(hostname);
}

// Above we leave the Encrypt option without a default, to let the provider to decide. But this
// routine permits the user to override.
void SetDefaultForEncryption(SV * sv_Encrypt,
                             SV * sv_Trust,
                             SV * sv_HostName,
                             SV * sv_ServerCert) {
   int    encrypt_ix = -1;
   int    trust_cert_ix = -1;
   int    host_name_ix  = -1;
   int    server_cert_ix = -1;

   // Loop over gbl_init_props to find the indexes for the Encrypt and TrustServerCert options
   for (int ix = 0; 
            ix < MAX_INIT_PROPERTIES && 
                 (encrypt_ix < 0 || trust_cert_ix < 0 || 
                  host_name_ix < 0 || server_cert_ix < 0); ix++) {
      if (gbl_init_props[ix].propset_enum == ssinit_props) {
         if (gbl_init_props[ix].property_id == SSPROP_INIT_ENCRYPT) {
            encrypt_ix = ix;
         }
         else if (gbl_init_props[ix].property_id == SSPROP_INIT_TRUST_SERVER_CERTIFICATE) {
            trust_cert_ix = ix;
         }
         else if (gbl_init_props[ix].property_id == SSPROP_INIT_HOST_NAME_CERTIFICATE) {
            host_name_ix = ix;
         }
         else if (gbl_init_props[ix].property_id == SSPROP_INIT_SERVER_CERTIFICATE) {
            server_cert_ix = ix;
         }
      }
   }

   // Sanity check.
   if (encrypt_ix == -1 || trust_cert_ix == -1 || host_name_ix == -1 || server_cert_ix == -1) {
      croak("Internal error! Was not able to set encrypt_ix(%d), trust_cert_ix(%d), host_name_ix(%d) or server_cert_ix(%d)!\n", 
            encrypt_ix, trust_cert_ix, host_name_ix, server_cert_ix);
   }
   
   // Handle Encrypt option
   if (sv_Encrypt && SvOK(sv_Encrypt)) {
      char * setting = SvPV_nolen(sv_Encrypt);

      if (strcmp("Optional", setting) == 0 || 
          strcmp("Mandatory", setting) == 0 || 
          strcmp("Strict", setting) == 0) {

         gbl_init_props[encrypt_ix].isoptional = FALSE;
         gbl_init_props[encrypt_ix].default_value.vt = VT_BSTR;
         gbl_init_props[encrypt_ix].default_value.bstrVal = SV_to_BSTR(sv_Encrypt);
      }
      else {
         warn("SetDefaultForEncrypt: Illegal value for encrypt option: '%s'.\n", setting);
         croak("Permitted values are undef, 'Optional', 'Mandatory', and 'Strict'.\n");
      }
   }
   else {
      gbl_init_props[encrypt_ix].isoptional = TRUE;
      VariantClear(&gbl_init_props[encrypt_ix].default_value);
   }

   // Handle default for TrustServerCertificate.
   if (sv_Trust && SvOK(sv_Trust)) {
      gbl_init_props[trust_cert_ix].isoptional = FALSE;
      gbl_init_props[trust_cert_ix].default_value.vt = VT_BOOL;
      gbl_init_props[trust_cert_ix].default_value.boolVal = 
           (SvTRUE(sv_Trust) ? -1 : FALSE);
   }
   else {
      gbl_init_props[trust_cert_ix].isoptional = TRUE;
      VariantClear(&gbl_init_props[trust_cert_ix].default_value);
   }

   // And host name in certificate.
   if (sv_HostName && SvOK(sv_HostName)) {
      gbl_init_props[host_name_ix].isoptional = FALSE;
      gbl_init_props[host_name_ix].default_value.vt = VT_BSTR;
      gbl_init_props[host_name_ix].default_value.bstrVal = SV_to_BSTR(sv_HostName);



( run in 0.382 second using v1.01-cache-2.11-cpan-71847e10f99 )