Teradata / PyTd

A Python Module to make it easy to script powerful interactions with Teradata Database in a DevOps friendly way.
MIT License
108 stars 43 forks source link

ODBC Library(libodbc.so) loading issue in AIX 7.1 #95

Open FrankWangAU opened 6 years ago

FrankWangAU commented 6 years ago

Env: OS AIX 7.1.0.0 tdodbc version: 14.10.0.7 xxxxxxxxxx:/opt/teradata/client/ODBC_64/lib: lslpp -l | grep odbc tdodbc1410.tdodbc1410 14.10.0.7 COMMITTED Teradata ODBC Driver for AIX

Tried to use PyTD to connect Teradata in AIX 7.1 but got following error: Traceback (most recent call last): File "/opt/freeware/lib64/python3.5/runpy.py", line 184, in _run_module_as_main "main", mod_spec) File "/opt/freeware/lib64/python3.5/runpy.py", line 85, in _run_code exec(code, run_globals) File "/home/xxxxxx/python/proj/cba/aipl/proj_teradata.py", line 71, in with ConnTest() as myConnTest: File "/home/xxxxxx/python/proj/cba/aipl/teradata/proj/init.py", line 1747, in init self.session = get_db_conn(config_files, query_band) File "/home/xxxxxx/python/proj/cba/aipl/teradata/utils/tdconn.py", line 166, in get_db_conn session = uda_exec.connect(queryBands=query_band, dataTypeConverter=TeradataDataTypeConverter(), conn_param) File "/home/xxxxxx/python/proj/lib64/python3.5/site-packages/teradata/udaexec.py", line 183, in connect args)) File "/home/xxxxxx/python/proj/lib64/python3.5/site-packages/teradata/tdodbc.py", line 399, in init init(odbcLibPath) File "/home/xxxxxx/python/proj/lib64/python3.5/site-packages/teradata/tdodbc.py", line 344, in init initOdbcLibrary(odbcLibPath) File "/home/xxxxxx/python/proj/lib64/python3.5/site-packages/teradata/tdodbc.py", line 297, in initOdbcLibrary odbc = ctypes.cdll.LoadLibrary(odbcLibPath) File "/opt/freeware/lib64/python3.5/ctypes/init.py", line 435, in LoadLibrary return self._dlltype(name) File "/opt/freeware/lib64/python3.5/ctypes/init.py", line 357, in init self._handle = _dlopen(self._name, mode) OSError: 0509-022 Cannot load module . 0509-026 System error: A file or directory in the path name does not exist. Did a little bit further investigation, then found there was an issue to load libodbc.so

Python 3.5.2 (default, Dec  1 2016, 02:07:23)
[GCC 4.8.5] on aix6
Type "help", "copyright", "credits" or "license" for more information.
>>> from ctypes import cdll
>>> print(cdll.LoadLibrary("libodbc.so"))
Loading Library: libodbc.so
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/freeware/lib64/python3.5/ctypes/__init__.py", line 437, in LoadLibrary
    return self._dlltype(name)
  File "/opt/freeware/lib64/python3.5/ctypes/__init__.py", line 357, in __init__
    self._handle = _dlopen(self._name, mode)
OSError:        0509-022 Cannot load module .
        0509-026 System error: A file or directory in the path name does not exist.
>>>

It turned out there is no file named libodbc.so, but there is one file named "odbc.so" exist in /opt/teradata/client/ODBC_64/lib

xxxxxxxxxx:/opt/teradata/client/ODBC_64/lib: ls -lrt
total 71192
-r--r--r--    1 bin      bin           67068 21 Mar 2015  vscnctdlg.so
-r--r--r--    1 bin      bin          229441 21 Mar 2015  tdconndlg.so
-r--r--r--    1 bin      bin         6020823 21 Mar 2015  tdata.so
-r--r--r--    1 bin      bin         2171709 21 Mar 2015  odbc.so
-r--r--r--    1 bin      bin         1830772 21 Mar 2015  odbcinst.so
-r--r--r--    1 bin      bin         2032897 21 Mar 2015  odbccurs.so
-r--r--r--    1 grandhsa 30000       1769226 21 Mar 2015  libtdsso.so
-r--r--r--    1 bin      bin          610880 21 Mar 2015  libtdparse.so
-r--r--r--    1 bin      bin          610880 21 Mar 2015  libtdparse.a
-r--r--r--    1 bin      bin         1831194 21 Mar 2015  libodbcinst.a
-r--r--r--    1 bin      bin         2172128 21 Mar 2015  libodbc.a
-r--r--r--    1 bin      bin        15005158 21 Mar 2015  libddicu26.a
-r--r--r--    1 bin      bin         2064704 21 Mar 2015  ddtrc26.so
lrwxrwxrwx    1 root     system           31 01 Aug 21:03 libicudatatd.so -> /usr/lib/lib_64/libicudatatd.so
lrwxrwxrwx    1 root     system           33 01 Aug 21:03 libicudatatd46.so -> /usr/lib/lib_64/libicudatatd46.so
lrwxrwxrwx    1 root     system           31 01 Aug 21:03 libicuiotd46.so -> /usr/lib/lib_64/libicuiotd46.so
lrwxrwxrwx    1 root     system           31 01 Aug 21:03 libicui18ntd.so -> /usr/lib/lib_64/libicui18ntd.so
lrwxrwxrwx    1 root     system           33 01 Aug 21:03 libicui18ntd46.so -> /usr/lib/lib_64/libicui18ntd46.so
lrwxrwxrwx    1 root     system           31 01 Aug 21:03 libiculetd46.so -> /usr/lib/lib_64/libiculetd46.so
lrwxrwxrwx    1 root     system           29 01 Aug 21:03 libicuiotd.so -> /usr/lib/lib_64/libicuiotd.so
lrwxrwxrwx    1 root     system           29 01 Aug 21:03 libiculetd.so -> /usr/lib/lib_64/libiculetd.so
lrwxrwxrwx    1 root     system           31 01 Aug 21:03 libicuuctd46.so -> /usr/lib/lib_64/libicuuctd46.so
lrwxrwxrwx    1 root     system           29 01 Aug 21:03 libiculxtd.so -> /usr/lib/lib_64/libiculxtd.so
lrwxrwxrwx    1 root     system           31 01 Aug 21:03 libiculxtd46.so -> /usr/lib/lib_64/libiculxtd46.so
lrwxrwxrwx    1 root     system           29 01 Aug 21:03 libicuuctd.so -> /usr/lib/lib_64/libicuuctd.so
lrwxrwxrwx    1 root     system           49 01 Aug 21:03 odbctrac.so -> /opt/teradata/client/14.10/odbc_64/lib/ddtrc26.so
lrwxrwxrwx    1 root     system           29 01 Aug 21:03 tdwalletdir -> /opt/teradata/client/tdwallet

So, I manually changed the code of function: initOdbcLibrary in tdodbc.py and added hack for AIX, now it is working fine.

def initOdbcLibrary(odbcLibPath=None):
    """Initialize the ODBC Library."""
    global odbc
    if odbc is None:
        if osType == "Windows":
            odbc = ctypes.windll.odbc32
        else:
            if not odbcLibPath:
                # If MAC OSx
                if osType == "Darwin":
                    odbcLibPath = "libiodbc.dylib"
                elif osType.startswith("CYGWIN"):
                    odbcLibPath = "odbc32.dll"
                # 2018-01-12 Bug fix for AIX teradata TTU 14.10
                if osType == "AIX":
                    odbcLibPath = "odbc.so"
                else:
                    odbcLibPath = 'libodbc.so'
            logger.info("Loading ODBC Library: %s", odbcLibPath)
            odbc = ctypes.cdll.LoadLibrary(odbcLibPath)

workaround codes:

                # 2018-01-12 Bug fix for AIX teradata TTU 14.10
                if osType == "AIX":
                    odbcLibPath = "odbc.so"

Should this workaround to be added in? Do we have testing in AIX flatform?

Thanks, Frank

escheie commented 6 years ago

Hi Frank,

I have not tested PyTd on AIX. You can set odbcLibPath without a code change by passing it as an argument to UdaExec or by specifying it in an external config file. I think we would only add the default value for AIX if it was officially tested on that platform. I will keep this issue open to track interest in officially certifying on AIX.

Thanks, -Eric

FrankWangAU commented 6 years ago

Hi Eric,

Did not know that we can specify "odbcLibPath" in external config file. I think I will go with this solution for now.

Thanks for your help.

Regards, Frank

djb18 commented 6 years ago

Hi Frank and Eric,

Glad to find this discussion, as I've also struggled to connect to Python to Teradata from AIX 7.1. Defining the odbcLibPath was critical, but I found it only works when placed in the udaexec.ini and NOT when passed as a UdaExec parameter.

I also found (in another post) that I had to define Authentication=LDAP. Strangely, this only works as a UdaExec parameter but NOT in the udaexec.ini (converse to the odbcLibPath)! Beyond the finicky location, this confuses me for other reasons... first, because the Teradata Python Module documentation doesn't include Authentication among UdaExec's valid parms, but also because I had already declared "Authentication=LDAP" in the odbc.ini config, within my DSN section. Why doesn't it work there?

Here is my sample Python program (which works): import teradata udaExec = teradata.UdaExec(appName="Test1", version="1.0") with udaExec.connect(method="odbc", authentication="LDAP", system="tdt", username="xxx", password="$$tdwallet(xxx)") as myses: ...

My udaexec.ini file: `[CONFIG] logConsole=True

[DEFAULT] odbcLibPath=/opt/teradata/client/ODBC_64/lib/odbc.so `

And finally, my /opt/teradata/client/ODBC_64/odbc.ini file: `[ODBC] InstallDir=/opt/teradata/client/ODBC_64 Trace=0 TraceDll=/opt/teradata/client/ODBC_64/lib/odbctrac.so TraceFile=/usr/joe/odbcusr/trace.log TraceAutoStop=0

[ODBC Data Sources] tdt=tdata.so

[tdt] Driver=/opt/teradata/client/ODBC_64/lib/tdata.so Description=Teradata Test database DBCName=tdt Authentication=LDAP Charset=UTF16`

Optimally, I want to place as much config as possible in external files to keep our programs cleaner, so I'd like to move the "method" and "authentication" to external configs, but attempting to do so breaks the db connection. Any insight you can offer is appreciated.

Thanks, Dan

FrankWangAU commented 6 years ago

Hi @djb18

I never use ODBC DSN as 'system', I always put all parameters into a external config file. you need ensure ODBC driver info in file: /opt/teradata/client/ODBC_64/odbcinst.ini is correct.

Frank

Soberoo commented 5 years ago

Hi Guys,

I am stuck on the same issue here - I tried to pass an alternative value of odbcLibPath, but in both cases (in config, and passing as a param), I get the same error:

using: odbcLibPath=/opt/teradata/client/ODBC_64/lib/odbc.

File "/opt/freeware/lib64/python3.5/teradata/udaexec.py", line 183, in connect **args)) TypeError: type object got multiple values for keyword argument 'odbcLibPath'

Any assistance gratefully received. Soberoo