2

I am trying to write data to a SQL Server 2016 Express database from an Ubuntu 16.04 virtual machine using python. The connection is to be set up via FreeTDS, UnixODBC, pyodbc.

The program gets a string of sensor data over a TCP socket, formats it and writes it to a (new or existing) table. This have to happen every x seconds. Therefor I used this answer.

The code evinces a strange behavior and I tried and searched for hours. It sometimes work and sometimes not. I think the problem is to do with connection = pyodbc.connect(conn_str), because often the code is successfully executed once and then gets stuck at this line. After some minutes an Error appears:

connection = pyodbc.connect(conn_str)
Error: ('08S01', '[08S01] [unixODBC][FreeTDS][SQL Server]Unable to connect: Adaptive Server is unavailable or does not exist (20009) (SQLDriverConnect)')

Maybe the problem correlates with an open connection but it should be closed at the end of the script. Do you have a solution? How can I catch such errors with a try/except funktion? Please ask if I should provide more information.

Thank you in advance for your help!

This is the code:

import sys
import pyodbc
import socket
import re
from threading import Timer
import time

# Timer (see link above)
class InfiniteTimer():

def pi_to_sql():
    dsn = "sqlserverdatasource"
    user = "User"
    password = "Password"
    database = "MSSQL_DB"

    conn_str = "DSN=%s;UID=%s;PWD=%s;DATABASE=%s;" % (dsn, user, password, database)
    connection = pyodbc.connect(conn_str)
    cursor = connection.cursor()

    # Create TCP/IP socket (client)
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # Create connection with socket server (insert IP of server)
    server_address = ('XXX.XXX.XXX.XXX', 10000)
    sock.connect(server_address)

    # Sending order to server
    to_send = "send data"
    sock.sendall(to_send)

    # Receiving data (string, e.g. "DS18B20;28FF97FF841605C0;32.4375")
    data = sock.recv(1024)

    # Filter data using regex
    data_list = re.findall(r"(?P<name>\w{6,});(?P<address>[0-9A-F]{16,});(?P<value>-?\d+\.?\d+)",data,re.M)

    # Closing socket
    sock.close()

    sensor_cnt = len(data_list)

    i = 0

    # Write sensor data to new or existing table using a loop
    while i<=(sensor_cnt-1):
        s_address = data_list[i][1]
        s_value = data_list[i][2]
        act_datetime = time.strftime("%Y%m%d %H:%M:%S")
        table_name = "sensor_" + s_address

        sql_command = """IF (NOT EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo'
AND  TABLE_NAME = '{0:s}'))
BEGIN
CREATE TABLE dbo.{0:s}
(
Timestamp DATETIME2(0),
S_Value FLOAT
)
END
INSERT INTO dbo.{0:s} VALUES
(
'{1:s}','{2:s}'
)""".format(table_name,act_datetime,s_value)

        try:
            cursor.execute(sql_command)
            connection.commit()

        except pyodbc.ProgrammingError:
            print("error")
            connection.close()

        i = i+1

    cursor.close()
    connection.close()

    print("done")

# Calling timer class to execute code every 5s
t = InfiniteTimer(5, pi_to_sql)
t.start()

tsql -C (I used the latest stable .tar.gz from FreeTDS)

Compile-time settings (established with the "configure" script)
                        Version: freetds v1.00.27
         freetds.conf directory: /usr/local/etc
 MS db-lib source compatibility: no
    Sybase binary compatibility: no
                  Thread safety: yes
                  iconv library: yes
                    TDS version: 7.4
                          iODBC: no
                       unixodbc: yes
          SSPI "trusted" logins: no
                       Kerberos: no
                        OpenSSL: no
                         GnuTLS: no
                           MARS: no

/etc/odbc.ini

[sqlserverdatasource]
driver = /usr/local/lib/libtdsodbc.so
server = XXX.XXX.XXX.XXX
port = 61521
database = MSSQL_DB
tds_version = 7.4

isql -v sqlserverdatasource User Password

+---------------------------------------+
| Connected!                            |
|                                       |
| sql-statement                         |
| help [tablename]                      |
| quit                                  |
|                                       |
+---------------------------------------+
SQL> 
Community
  • 1
  • 1
E.Mtt
  • 69
  • 1
  • 9
  • Have you tried monitoring the SQL Server instance to see if it is accumulating a significant number of old stale connections? Have you tried experimenting with [connection pooling](http://www.unixodbc.org/doc/conn_pool.html) to see if that makes any difference? Also, as an aside, do you have a compelling reason for creating multiple identical tables for each sensor instead of a single table that records the sensor address in a column? – Gord Thompson Apr 04 '17 at 15:54
  • The process appears in the Activity Monitor of the SQL Server Management Studio for a short time, but only if the connection was successful. Then the `sql_command` is shown in the details. If the program is working every e.g. 5s, the process isn´t displayed. Is there a possibility to view incoming connection querys? The editing of the obcdinst.ini doesn´t makes a change. I am not shure if the obcdinst.ini is even used, because I configured everything in the obcd.ini (see this [tutorial](https://techblog.jj-it.de/oracle-gateway-to-odbc/access-to-ms-sql-server-database-freetds-installation/)). – E.Mtt Apr 05 '17 at 05:46
  • But first of all thank you for the fast response! Creating one table for each sensor is part of my task. Do you think that this can cause problems? – E.Mtt Apr 05 '17 at 05:55

2 Answers2

0

This thread gave me the idea to test the code on a local server with another version (SQL Server 2014) where I have administrator rights. So I ensured that for each listed IP address, Active and Enabled are both Yes. It is working now. Unfortunately I can´t say excactly what the problem was (if it is up to SQL Server 16 itself or the configs or an incompatibility). I hope that this remark possibly helps somebody.

Community
  • 1
  • 1
E.Mtt
  • 69
  • 1
  • 9
-1

Have you tried it without connection pooling?

conn_str = "DSN=%s;UID=%s;PWD=%s;DATABASE=%s;" % (dsn, user, password, database)
pyodbc.pooling = False
connection = pyodbc.connect(conn_str)
cursor = connection.cursor()

I've seen that clear up connection issues with SQL Server in the past because of this:

https://github.com/mkleehammer/pyodbc/wiki/Features-beyond-the-DB-API#connection-pooling

Good luck!

FlipperPA
  • 13,607
  • 4
  • 39
  • 71
  • Thank you for the answer. Unfortunately this makes no difference. It is crazy. Sometimes I get about 1000 rows straight (one every second), then it gets stuck and cannot be restarted for a while till it works again (respectively only one time) in a wondrous way. Because of this it is hard to confine the problem. – E.Mtt Apr 05 '17 at 13:38