Re: ODBC bcp

Frediano Ziglio <[email protected]>
Newsgroups gmane.comp.db.tds.freetds
Message-ID <CAHt6W4fRrYc_UaivVoCok2MDFa6haTqLop_cmPZC5xSHXdZcmQ@mail.gmail.com>
2015-03-12 19:36 GMT+00:00 Richard Hughes <[email protected]>:
> Hi all,
>
> I wanted to be able to bulk copy into SQL Server from an ODBC
> application, so attached is my *work in progress* attempt at making
> this happen, for the purpose of early feedback about the approach.
>

Very interesting project! I thought about sometimes but never had a
practical reason to do it!

> The only other attempt I can find at doing this was [0], which seemed
> extremely hacky and may have required significant code in the user
> application. My attempt tries to do it 'properly' and I've been
> testing it with an application which builds unaltered on both Windows
> and Linux.
>

Yes, I agree too, no change to user application (beside perhaps some
include and obviously tools changed).

> The patch steals a large amount of code from dblib/bcp.c but, in
> order to give myself an achievable goal, it implements *only* bcp in
> from RAM; all of the file I/O stuff has been ripped out. I believe
> this is a very sane and useful subset to have because my application
> does the bcp as one step of a single larger transaction and hence
> needs to use the existing ODBC connection. People wanting the file I/O
> bit probably don't have this constraint and hence can just use dblib
> or freebcp.
>
> A very useful document is [1] which lists the API differences from
> dblib. The most annoying is probably that all errors must go through
> SQLGetDiagRec rather than dblib's error handling, which is what
> necessitated so much copy-and-paste. A future task is to look at how
> this could be rationalized.
>

Yes, coy and paste is not that great. I'm changing actually bcp.c
(dblib) to use tds_convert instead of dbconvert. This could help
moving code to libTDS (the common part) so we can reuse it. I remember
was a nightmare extracting code from either dblib and ctlib in libTDS
I don't want to do it again for dblib/odbc :)

Well... probably using tdserror instead of dbperror could be a way.
libTDS use callbacks and even for normal errors from network if calls
these callbacks. In odbc these callbacks store errors in
statement/connection to be retrieved by SQLGetDiagRec. I noted that
bcp.c (used objdump) calls very few dblib functions (dbconvert,
dbvarylen, dbperror and another I don't remember now) so I don't think
will be much effort to move to libTDS. In bulk.c or another file.

> The HDBC problem should be discussed. Because the driver manager has
> no idea about the bcp APIs, the first parameter that all the functions
> receive is a driver manager HDBC, not a TDS_DBC*.
> SQLGetInfo(SQL_DRIVER_HDBC) can be used to convert, but that means we
> need to find SQLGetInfo. Approaches I've thought of so far are:
>
> 1) dlopen(...,RTLD_NOLOAD) with various hard-coded filenames to try to
> find which driver manager the application is using
>
> 2) dl_iterate_phdr() to look for filenames that look like the driver
> manager [this is what's currently in the patch].
>
> 3) dlsym(RTLD_DEFAULT,...).
>
> 4) Create a gratuitous libtdsodbcbcp.so containing just thunker
> functions whose sole purpose is to get out of the way of the default
> symbol search path so that method (3) works more reliably.
>
> Microsoft's Windows driver uses (1) because they know there's only one
> driver manager. (3) is subject to ordering problems with the symbol
> search, but those problems exist anyway at link time so I'm beginning
> to think that might be best.
>

(3) should be fine as odbc drivers (like our one) should be loaded
with RTLD_LOCAL flag so the SQLGetInfo from our driver is not found
with RTLD_DEFAULT.

Another idea would be to use a sort of "tunnel" using functions like
SQLSetConnectAttr. Practically let's say we want to implement
bcp_collen, we do something like

typedef struct  {
  DBINT cbData;
  INT idxServerCol;
} bcp_collen_params;

RETCODE bcp_collen (
        HDBC hdbc,
        DBINT cbData,
        INT idxServerCol)
{
   bcp_collen_params params = { cbData, idxServerCol };
    return SQLSetConnectAttr(hdbc, SQL_SS_FUNC_BCP_COLLEN, &params,
sizeof(params));
}

and FreeTDS code detect the willing to use bcp_collen and call
tdsodbc_bcp_collen according. This way you solve having to get proper
HDBC (driver one) and resolving tdsodbc_bcp_collen. Consider also that
driver could be unloaded and loaded in another position but this way
is you don't have to cache a pointer to tdsodbc_bcp_collen to make it
faster the call. Yes, you have to marshal and unmarshal the
parameters.

> Richard.
>
> [0] http://lists.ibiblio.org/pipermail/freetds/2005q1/018076.html
> [1] https://msdn.microsoft.com/en-us/library/ms130924.aspx

Frediano
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.