Linking
When linking a program that uses SQLAPI++ I got an error: unresolved external symbol "__declspec(dllimport) public: virtual __thiscall ..."
What is missing?

Probably you forgot to include sqlapi.lib (or other SQLAPI++ library, depends on the compiler and/or static/dynamic version) into the project. To link with SQLAPI++ library you have to include sqlapi.lib into you project (as well as *.cpp)

For example, in Visual C++ you have to do the following:

  1. In workspace tree select the project that you link with SQLAPI++
  2. Use "Add Files to Project..." command on the right click menu and add sqlapi.lib to you project
  3. Press F7 button to compile and link you project
  • sqlapi.lib should be used when linking with dynamic version of SQLAPI++ using Microsoft Visual C++
  • sqlapis.lib to link with a static version using MSVC++
  • sqlapib.lib to link with a dynamic version using Borland C++ compiler
  • sqlapibs.lib to link with a static version using Borland C++ compiler
  • sqlapibb.lib to link with a dynamic version using Borland C++ Builder compiler
  • sqlapibbs.lib to link with a static version using Borland C++ Builder compiler
  • To link with a debug version use appropriate lib file that ends with 'd'
Sybase Stored Procedure
How can I get status return code from stored procedure on Sybase?

SQLAPI++ library automatically creates SAParam object to represent procedure status return code.

After a command execution you can check it value. This SAParam object's name is "RETURN_VALUE", object's type is SA_dtLong, object's direction type is SA_ParamReturn.

Sybase Stored Procedure
I have a stored procedure in Sybase ASE that has an input-output parameter. But when I call SAParam::ParamDirType method for this parameter it always returns SA_ParamInput, and when I try to get the parameter's value after the command execution I receive nothing.
What is wrong?

Sybase ASE server does not provide information about parameter's direction type, that's why SQLAPI++ library defines all parameters (except status result code) as input (SA_ParamInput). If you have input-output parameters in the procedure you have to call SAParam::setParamDirType method for these parameters before command execution and set parameter's direction type explicitly (SA_ParamInputOutput).

You shouldn't call this method for procedure status result code because it is detected correctly (as SA_ParamReturn) by the library.

There is no need to call this method for Sybase ASA because all parameters are detected correctly automatically (including the direction type).

Sybase Connection
Sometimes "sybinit.err" file is appended to at a successful SQLAPI++ connection to ASE/ASA using Open Client? Console application also outputs some warning(s) about context allocation failure.
Is this indicative of a real problem I should worry about?

SQLAPI++ has to set the version of Client-Library behavior when it connects to Sybase. By default SQLAPI++ tries to set 12.5 behavior; if it fails, SQLAPI++ tries to set 11.0 behavior and so on from the highest version to the lowest one until it succeeds. Finally the connection is successful, but every time when it fails Sybase server generates the appropriate error message and adds the same error message to the "sybinit.err" file.

The solution is to set the CS_VERSION option to "Detect" using SAConnection::setOption method. In that case SQLAPI++ automatically detects the maximum available version of behavior via the version of Client-Library.

RowsAffected
I execute a "Select..." statement and then call SACommand::RowsAffected() method to determine the number of rows to be returned by the command, but the result is always incorrect (-1 or 0 most of the times).
What is wrong?
SACommand::RowsAffected is only applicable to INSERT/UPDATE/DELETE queries. It is database API limitation, not SQLAPI++'s. The only way to get to know result set size in advance is to execute "Select count(*)..." command.
Linking Visual Studio
When linking a program that uses SQLAPI++ under Microsoft Visual Studio 2005 I got an error: error LNK2001: unresolved external symbol "public: __thiscall SAString::SAString(wchar_t const *)"
I do include sqlapiu.lib in the project. What's wrong?

We provide SQLAPI++ binaries that are linked with Microsoft Visual Studio 6. Visual C++ 2005 compiler turns the /Zc:wchar_t option on by default. This is incompatible with Visual C++ 6.

To avoid this problem you can turn off /Zc:wchar_t option (use /Zc:wchar_t- or look at the project options dialog ('Treat wchar_t as Build-in Type' item).

But the best option is to rebuild SQLAPI++ with Visual C++ 2005 compiler.

Linking Visual Studio
When linking a program with static SQLAPI++ library I got an error: sqlapis.lib(samisc.obj) : error LNK2019: unresolved external symbol _GetFileVersionInfoA@16 referenced in function "void * __cdecl SAGetVersionInfo(char const *)" (?SAGetVersionInfo@@YAPAXPBD@Z)
Do I need to include additional libraries to resolve this?
Since SQLAPI++ 3.7.25 you also need the libraries: user32.lib, version.lib, oleaut32.lib and ole32.lib
SQL Server Connection Linux
How can I connect to Microsoft SQL Server from Linux using SQLAPI++?

There is an SQL Server ODBC/SQLNCLI Driver for Linux that provides native connectivity from Linux to Microsoft SQL Server.

SQLAPI++ support this client software but Microsoft releases this driver only for x86-64 Linux platforms. When you want to run the software on 32-bit Linux or any other Unix-like system you must use ODBC connection. You need ODBC driver manager (most known are iODBC or unixODBC) and MSSQL ODBC driver (FreeTDS ODBC driver or commercial EasySOFT one).

You must install and configure ODBC software. Often Linux distribution maintainers split the software into several packages, for example Debian Lenny includes the next iODBC and FreeTDS packages:

  • iodbc - GTK+ config frontend for the iODBC Driver Manager
  • libiodbc2-dev - iODBC Driver Manager (development files)
  • libiodbc2 - iODBC Driver Manager
  • freetds-common - configuration files for FreeTDS SQL client libraries
  • freetds-dev- MS SQL and Sybase client library (static libs and headers)
  • libct4 - libraries for connecting to MS SQL and Sybase SQL servers
  • libsybdb5 - libraries for connecting to MS SQL and Sybase SQL servers
  • tdsodbc - ODBC driver for connecting to MS SQL and Sybase SQL servers

It's enough to have installed only iodbc, libiodbc2, freetds-common and tdsodbc packages. For the first time it's better to configure and test DSN (any driver manager includes console tool for command line SQL):

bedlam:~# cat ~/.odbc.ini
[ODBC Data Sources]
MSSQL = FreeTDS driver
[MSSQL]
Driver = /usr/lib/odbc/libtdsodbc.so
Server = BEDLAM-M\SQLEXP2008EN
Database = pubs
TDS_Version = 7.0

bedlam:~# cat ~/.odbcinst.ini
[ODBC Drivers]
FreeTDS driver = Installed
[FreeTDS driver]
Driver = /usr/lib/odbc/libtdsodbc.so
Setup = /usr/lib/odbc/libtdsS.so

iodbc package provides iodbctest tool I run with parameters to check the ODBC DSN status:

bedlam-home:~# iodbctest "DSN=MSSQL;UID=sa;PWD=xxx"
iODBC Demonstration program
This program shows an interactive SQL processor
Driver Manager: 03.52.0607.1008
Driver: 0.82 (libtdsodbc.so)

SQL>

Then you can connect using SQLAPI++:

con.Connect("MSSQL", "sa", "xxx", SA_ODBC_Client);

or without DSN configured:

conn.Connect("DRIVER={FreeTDS driver};SERVER=BEDLAM-M\SQLEXP2008EN;DATABASE=pubs;TDS_Version=7.0", "sa", "xxx", SA_ODBC_Client);

RowsAffected
Is there a way to get the number of rows returned by a query?
Generally it's impossible. Most DBMS can count the result set rows only when all rows are already cached at the client side. For such cases SACommand::RowsAffected method returns the result set row count.
Source Code Windows
I got the .diff files, but how can I apply them to my existing source code? Could you please help me?

You need a GNU patch tool. Version for Win32 can be downloaded from SouceForge. Then run it from the console like:

C:\SQLAPI\src>patch.exe < myClient.cpp.diff
patching file myClient.cpp

C:\SQLAPI\src>
Linking Linux
When compiling step1.cpp on Linux got an error: libsqlapi.so: undefined reference to `dlerror', 'dlopen', 'dlsym'...
Could you help?

SQLAPI++ uses libdl library API to load DBMS API. So you should include this library when you linking your binary file with SQLAPI, e.g.:

g++ -I../inclide -L../lib step1.cpp -o step1.out -lsqlapi -ldl

or with static SQLAPI without libpath:

g++ -I../inclide step1.cpp -o step1.out ../lib/libsqlapi.a -lsqlapi -ldl

MySQL SACommand SAConnection
Could I have multiple instances of SACommand in one SAConnection? I tested under MySQL database, keep SACommands being opened, and always get error "Commands out of sync; you can't run this command now".

Default MySQL API doesn't allow to have several opened result set per connection. This is exactly what mysql_use_result does. The connection is blocked until you fetch all result set rows. When you'd like to run another command when there is not fetched result set at the same connection you must use another solution. The simplest - using MySQL 'store' result set:

SACommand cmd(&con, "select ...");
cmd.setOption("HandleResult") = "store";
cmd.Execute();
...

Then SQLAPI uses mysql_store_result and all rows cached at he client side after query is executed and you can fetch the result and execute another command at the same connection. The not good thing - this solution uses local MySQL client data cache. So MySQL guys added another feature - statement API. statement API allows to execute command but the result set stored at the client side. SQLAPI++ can use this feature with 'SELECT' commands. The limitation - the single, forward-only result set only:

SACommand cmd(&con, "select ...");
cmd.setOption("UseStatement") = "TRUE";
cmd.Execute();
...

SQL Server SACommand SAConnection
I am getting the error "HY000 [Microsoft][SQL Server Native Client 11.0] Connection is busy with results for another command".
"I have 1 SAConnection object and 2 SACommand objects. One of the SACommand objects is simply stepping through the records in a table. The second object is attempting to INSERT a new record into the same table, before we have processed all of the records in the first command.
Is this not allowed or am I doing something wrong?"

This is known SQL Server issue. SQL Server doesn't allow to have the several result sets at the same connection by default (the same problem is actual also for Sybase ASE, MySQL, ...). The possible solutions for SQL Server:

  • using MARS_Connection option. This is global option for the connection. You can turn it on like:
    SAConnection con;
    con.Connect(_TSA("bedlam-m\\sql2014en@test;MARS_Connection=yes"), _TSA(""),
    _TSA(""), SA_SQLServer_Client);
  • using the server side cursor for executing SACommand:
    SACommand cmd(&con, _TSA("select ..."));
    cmd.setOption(_TSA("SQL_ATTR_CURSOR_TYPE")) = _TSA("SQL_CURSOR_DYNAMIC");
isAlive isConnected
Could you please tell me what is the difference between isAlive and isConnected? How to use them properly?

See below the simplified usage scenario:

while(....)
    {
        try
        {
            if(!con.isConnected())
                con.Connect(...);
            // database operations below
            ...
        }
        catch(SAException& x)
        {
            if(con.isConnected())
            {
                if(!con.isAlive())
                {
                    try {
                        con.Disconnect();
                    } catch(SAException&) {
                        con.Destroy();
                    }
                }
            }
        }
    }
Linux violation
During the test of SQLAPI++ we encountered a SEGV violation inside 3rd party ODBC library during normal process exit (return).
#0  0x0000000000000000 in ?? ()
Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7.x86_64 keyutils-libs-1.5.8-3.el7.x86_64 krb5-libs-1.15.1-34.el7.x86_64 libaio-0.3.109-13.el7.x86_64 libcom_err-1.42.9-13.el7.x86_64 libgcc-4.8.5-36.el7.x86_64 libgsasl-1.8.0-8.el7.x86_64 libidn-1.28-4.el7.x86_64 libntlm-1.3-0.6.el7.x86_64 libselinux-2.5-14.1.el7.x86_64 libstdc++-4.8.5-36.el7.x86_64 libuuid-2.23.2-59.el7.x86_64 libxml2-2.9.1-6.el7_2.3.x86_64 numactl-libs-2.0.9-7.el7.x86_64 openssl-libs-1.0.2k-16.el7.x86_64 pcre-8.32-17.el7.x86_64 protobuf-2.5.0-8.el7.x86_64 xz-libs-5.2.2-1.el7.x86_64 zlib-1.2.7-18.el7.x86_64
(gdb) where
#0  0x0000000000000000 in ?? ()
#1  0x00007f885bc747b8 in CFmsStatus::Log (this=this@entry=0xf61650, toStdErr=1 '\001', toLog=toLog@entry=1) at /home/mnardi/devel/erm-server/ref/interfaces/fmsinterface/fmsstatus.cpp:1046
#2  0x0000000000417b84 in sigSegV (sig=<optimized out>) at /home/mnardi/devel/erm-server/ref/interfaces/fmsvarchive/ermvarchive_server.cpp:1109
#3  <signal handler called>
#4  0x00007f8843d3158e in client_dispose () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#5  0x00007f8843d3446b in sasl_dispose () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so#6  0x00007f8843cabf61 in sasl::TSaslClient::~TSaslClient() () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#7  0x00007f8843caaa6b in boost_sb::detail::sp_counted_impl_p<sasl::TSaslClient>::dispose() () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#8  0x00007f8843cac3a3 in apache::thrift::transport::TSaslTransport::~TSaslTransport() () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#9  0x00007f8843caab18 in Simba::ThriftExtension::TETSaslClientTransport::~TETSaslClientTransport() () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#10 0x00007f8842f3121f in boost_sb::detail::sp_counted_impl_p<Simba::ThriftExtension::TETSaslClientTransport>::dispose() () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#11 0x00007f8842f3211a in apache::thrift::protocol::TBinaryProtocolT<apache::thrift::transport::TTransport>::~TBinaryProtocolT() () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#12 0x00007f8842f31245 in boost_sb::detail::sp_counted_impl_p<apache: <apache::thrift::transport::TTransport> >::dispose() () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#13 0x00007f8842ec07dd in boost_sb::detail::sp_counted_base::release() () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#14 0x00007f8843050479 in apache::hive::service::cli::thrift::TCLIServiceClient::~TCLIServiceClient() () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#15 0x00007f8842fa5ce5 in Simba::Hardy::HardyTCLIServiceThreadSafeClient::~HardyTCLIServiceThreadSafeClient() () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#16 0x00007f8842fa5d19 in Simba::Hardy::HardyTCLIServiceThreadSafeClient::~HardyTCLIServiceThreadSafeClient() () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#17 0x00007f8842f369ed in Simba::Hardy::HardyHiveCxnPool::~HardyHiveCxnPool() () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#18 0x00007f8842f2ca2b in Simba::Hardy::HardyHiveClientFactory::~HardyHiveClientFactory() () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#19 0x00007f8842eaee0f in Simba::Hardy::HardyConnection::Disconnect() () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#20 0x00007f884342b34c in Simba::ODBC::ConnectionState4::SQLDisconnect(Simba::ODBC::Connection*) () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#21 0x00007f884341f1df in Simba::ODBC::Connection::SQLDisconnect() () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#22 0x00007f88433e51ba in short DoTask<Simba::ODBC::SQLDisconnectTask>(char const*, void*, Simba::ODBC::SQLDisconnectTask::TaskParameters&) () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#23 0x00007f88433d3b6c in SQLDisconnect () from /opt/cloudera/hiveodbc/lib/64/libclouderahiveodbc64.so
#24 0x00007f885d644a82 in SQLDisconnect (connection_handle=0x108c930) at SQLDisconnect.c:351
#25 0x00007f885d37d21a in IodbcConnection::Destroy (this=0x10afd80) at odbcClient.cpp:594
#26 0x00007f885d34b8f4 in SAConnection::Destroy (this=0x10996c0) at SQLAPI.cpp:1556
#27 0x00007f885d349c2c in SAPI::~SAPI (this=0x7f885d6318a0 <g_odbcAPI>, __in_chrg=<optimized out>) at SQLAPI.cpp:863
#28 0x00007f885a48eeda in __cxa_finalize () from /lib64/libc.so.6
#29 0x00007f885d348be3 in __do_global_dtors_aux () from /app/ermqa/erm/lib/libsqlapi.so
#30 0x00007ffe0df36670 in ?? ()
#31 0x00007f886197cfca in _dl_fini () from /lib64/ld-linux-x86-64.so.2
Backtrace stopped: frame did not save the PC
(gdb) quit

If you use 'exit(x)' function in your code that terminates the main process then local object destructors are not called and related SACommand/SAConnection are still opened. But C++ library processes exit() call and destroys all global objects - such as default SAPI ones used inside SQLAPI.

This doesn't look like a problem but since SQLAPI++ 5.x this global SAPI decides to close/destroy all linked SAConnection/SACommand. Meanwhile, it does this _after_ loaded DBMS API can clear its resources using atexit(). In the end, DBMS API function is called _after_ all resources are cleaned and almost any such function crashes.

You must clear all SQLAPI++ related resources (by executing SAPI::setClient(SA_Client_NotSpecified) for global/default SAPI instances) before calling exit(x).