duckdb / duckdb-odbc

ODBC Driver for DuckDB
https://duckdb.org/docs/api/odbc/overview
10 stars 4 forks source link

SQLDriverConnect throws an exception instead of returning an error when database is already open for writing in another process #4

Closed PeterAronson closed 5 months ago

PeterAronson commented 5 months ago

What happens?

When attempting to open a DuckDB database for writing that is already open for writing in another process, the DuckDB ODBC driver throws an exception instead of returning SQL_ERROR. This is incorrect as ODBC is a C API, not a C++ API.

To Reproduce

Open a database in the DuckDB command line.

Execute the below program with a DSN of the general form:

"DRIVER={DuckDB Driver};Database=C:\xxx\experiment1.duckdb"

Using the same database.

Program:

#include <string>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#include <io.h>
#include <windows.h>
#include <conio.h>
#endif /*WIN32*/

#include "sqlext.h"

int main (long         argc,
          char*        argv[])
{

  /* Handles */
  SQLHDBC     hdbc1;
  SQLHENV     henv1;

  /* Miscellaneous variables */
  SQLCHAR       dsn[1024];
  SQLRETURN     rc = 0;

  if (argc == 2)
  {
    /* Use specified dsn */
    strcpy ((char *)dsn, (char *)argv[1]);
    fprintf (stdout, "Using specified DSN : %s\n", dsn);
  }
  else
  {
    fprintf (stdout, "Usage : %s <dsn>\n", argv[0]);
    exit(0);
  }

  /* Connection. */

  rc = SQLAllocHandle (SQL_HANDLE_ENV,SQL_NULL_HANDLE,&henv1);
  if (rc != SQL_SUCCESS)
  {
    fprintf (stdout,"Environment Handle Allocation failed (%d)\nExiting!!",rc);
    return (1);
  }

  rc = SQLSetEnvAttr (henv1,SQL_ATTR_ODBC_VERSION,(SQLPOINTER)SQL_OV_ODBC3,0);
  if (rc != SQL_SUCCESS)
  {
    fprintf (stdout,"SQLSetEnvAttr failed (%d)\nExiting!!",rc);
    return (1);
  }

  rc = SQLAllocHandle (SQL_HANDLE_DBC,henv1,&hdbc1);
  if (rc != SQL_SUCCESS)
  {
    fprintf (stdout,"Connection Handle Allocation failed (%d)\nExiting!!",rc);
    return (1);
  }

  fprintf (stdout,"First connect attempt...\n");

  try {
    rc = SQLDriverConnect (hdbc1,NULL,dsn,SQL_NTS,(SQLCHAR *)NULL,(SQLSMALLINT)0,(SQLSMALLINT *)NULL,(SQLSMALLINT)0);
    if (rc != SQL_SUCCESS)
    {
      fprintf (stdout,"SQLDriverConnect failed (%d)\nExiting!!",rc);
      return (1);
    }
  } catch (const std::exception& e) {
    fprintf (stdout,"Exception %s caught on attempted connect.\n",e.what());
    return (1);
  }

  fprintf (stdout,"First connection succeeded...\n");

  /* Done. */

  SQLDisconnect (hdbc1);

  SQLFreeHandle (SQL_HANDLE_DBC,hdbc1);
  SQLFreeHandle (SQL_HANDLE_ENV,henv1);

  return (0);
}

Resulting output:

Using specified DSN : DRIVER={DuckDB Driver};Database=C:\Users\xxx\experiment1.duckdb
First connect attempt...
Exception {"exception_type":"IO","exception_message":"File is already open in \nC:\\xxx\\AppData\\Local\\Microsoft\\WinGet\\Packages\\DuckDB.cli_Microsoft.Winget.Source_8wekyb3d8bbwe\\duckdb.exe (PID 267636)"} caught on attempted connect.

OS:

Windows 10 Enterprise, x64

DuckDB Version:

0.10.2 and 5/28/2024 bleeding edge

DuckDB Client:

ODBC

Full Name:

Peter Aronson

Affiliation:

Esri

What is the latest build you tested with? If possible, we recommend testing with the latest nightly build.

I have tested with a nightly build

Did you include all relevant data sets for reproducing the issue?

Yes

Did you include all code required to reproduce the issue?

Did you include all relevant configuration (e.g., CPU architecture, Python version, Linux distribution) to reproduce the issue?