Merge pull request #5202 from taosdata/feature/sangshuduo/TD-2971-python-connector-support-unsigned

[TD-2971] feature: make python connector support unsigned type.
This commit is contained in:
Shuduo Sang 2021-02-19 11:23:50 +08:00 committed by GitHub
commit 78af455071
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1038 additions and 304 deletions

View File

@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
setuptools.setup( setuptools.setup(
name="taos", name="taos",
version="2.0.5", version="2.0.6",
author="Taosdata Inc.", author="Taosdata Inc.",
author_email="support@taosdata.com", author_email="support@taosdata.com",
description="TDengine python client package", description="TDengine python client package",

View File

@ -3,12 +3,12 @@ from .connection import TDengineConnection
from .cursor import TDengineCursor from .cursor import TDengineCursor
# Globals # Globals
apilevel = '2.0.3'
threadsafety = 0 threadsafety = 0
paramstyle = 'pyformat' paramstyle = 'pyformat'
__all__ = ['connection', 'cursor'] __all__ = ['connection', 'cursor']
def connect(*args, **kwargs): def connect(*args, **kwargs):
""" Function to return a TDengine connector object """ Function to return a TDengine connector object
@ -21,4 +21,4 @@ def connect(*args, **kwargs):
@rtype: TDengineConnector @rtype: TDengineConnector
""" """
return TDengineConnection(*args, **kwargs) return TDengineConnection(*args, **kwargs)

View File

@ -4,11 +4,14 @@ from .error import *
import math import math
import datetime import datetime
def _convert_millisecond_to_datetime(milli): def _convert_millisecond_to_datetime(milli):
return datetime.datetime.fromtimestamp(milli/1000.0) return datetime.datetime.fromtimestamp(milli / 1000.0)
def _convert_microsecond_to_datetime(micro): def _convert_microsecond_to_datetime(micro):
return datetime.datetime.fromtimestamp(micro/1000000.0) return datetime.datetime.fromtimestamp(micro / 1000000.0)
def _crow_timestamp_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_timestamp_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C bool row to python row """Function to convert C bool row to python row
@ -18,168 +21,309 @@ def _crow_timestamp_to_python(data, num_of_rows, nbytes=None, micro=False):
_timestamp_converter = _convert_microsecond_to_datetime _timestamp_converter = _convert_microsecond_to_datetime
if num_of_rows > 0: if num_of_rows > 0:
return list(map(_timestamp_converter, ctypes.cast(data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)])) return list(map(_timestamp_converter, ctypes.cast(
data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)]))
else: else:
return list(map(_timestamp_converter, ctypes.cast(data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)])) return list(map(_timestamp_converter, ctypes.cast(
data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)]))
def _crow_bool_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_bool_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C bool row to python row """Function to convert C bool row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele == FieldType.C_BOOL_NULL else bool(ele) for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_byte))[:abs(num_of_rows)] ] return [
None if ele == FieldType.C_BOOL_NULL else bool(ele) for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_byte))[
:abs(num_of_rows)]]
else: else:
return [ None if ele == FieldType.C_BOOL_NULL else bool(ele) for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_bool))[:abs(num_of_rows)] ] return [
None if ele == FieldType.C_BOOL_NULL else bool(ele) for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_bool))[
:abs(num_of_rows)]]
def _crow_tinyint_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_tinyint_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C tinyint row to python row """Function to convert C tinyint row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele == FieldType.C_TINYINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_byte))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_TINYINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_byte))[:abs(num_of_rows)]]
else: else:
return [ None if ele == FieldType.C_TINYINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_byte))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_TINYINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_byte))[:abs(num_of_rows)]]
def _crow_tinyint_unsigned_to_python(
data,
num_of_rows,
nbytes=None,
micro=False):
"""Function to convert C tinyint row to python row
"""
if num_of_rows > 0:
return [
None if ele == FieldType.C_TINYINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_byte))[
:abs(num_of_rows)]]
else:
return [
None if ele == FieldType.C_TINYINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_byte))[
:abs(num_of_rows)]]
def _crow_smallint_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_smallint_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C smallint row to python row """Function to convert C smallint row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele == FieldType.C_SMALLINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_short))[:abs(num_of_rows)]] return [
None if ele == FieldType.C_SMALLINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_short))[
:abs(num_of_rows)]]
else: else:
return [ None if ele == FieldType.C_SMALLINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_short))[:abs(num_of_rows)] ] return [
None if ele == FieldType.C_SMALLINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_short))[
:abs(num_of_rows)]]
def _crow_smallint_unsigned_to_python(
data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C smallint row to python row
"""
if num_of_rows > 0:
return [
None if ele == FieldType.C_SMALLINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_short))[
:abs(num_of_rows)]]
else:
return [
None if ele == FieldType.C_SMALLINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_short))[
:abs(num_of_rows)]]
def _crow_int_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_int_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C int row to python row """Function to convert C int row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele == FieldType.C_INT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_int))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_INT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_int))[:abs(num_of_rows)]]
else: else:
return [ None if ele == FieldType.C_INT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_int))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_INT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_int))[:abs(num_of_rows)]]
def _crow_int_unsigned_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C int row to python row
"""
if num_of_rows > 0:
return [
None if ele == FieldType.C_INT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_int))[
:abs(num_of_rows)]]
else:
return [
None if ele == FieldType.C_INT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_int))[
:abs(num_of_rows)]]
def _crow_bigint_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_bigint_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C bigint row to python row """Function to convert C bigint row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele == FieldType.C_BIGINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_BIGINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)]]
else: else:
return [ None if ele == FieldType.C_BIGINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_BIGINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)]]
def _crow_bigint_unsigned_to_python(
data,
num_of_rows,
nbytes=None,
micro=False):
"""Function to convert C bigint row to python row
"""
if num_of_rows > 0:
return [
None if ele == FieldType.C_BIGINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_long))[
:abs(num_of_rows)]]
else:
return [
None if ele == FieldType.C_BIGINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_long))[
:abs(num_of_rows)]]
def _crow_float_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_float_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C float row to python row """Function to convert C float row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if math.isnan(ele) else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_float))[:abs(num_of_rows)] ] return [None if math.isnan(ele) else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_float))[:abs(num_of_rows)]]
else: else:
return [ None if math.isnan(ele) else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_float))[:abs(num_of_rows)] ] return [None if math.isnan(ele) else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_float))[:abs(num_of_rows)]]
def _crow_double_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_double_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C double row to python row """Function to convert C double row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if math.isnan(ele) else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_double))[:abs(num_of_rows)] ] return [None if math.isnan(ele) else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_double))[:abs(num_of_rows)]]
else: else:
return [ None if math.isnan(ele) else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_double))[:abs(num_of_rows)] ] return [None if math.isnan(ele) else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_double))[:abs(num_of_rows)]]
def _crow_binary_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_binary_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C binary row to python row """Function to convert C binary row to python row
""" """
assert(nbytes is not None) assert(nbytes is not None)
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele.value[0:1] == FieldType.C_BINARY_NULL else ele.value.decode('utf-8') for ele in (ctypes.cast(data, ctypes.POINTER(ctypes.c_char * nbytes)))[:abs(num_of_rows)]] return [None if ele.value[0:1] == FieldType.C_BINARY_NULL else ele.value.decode(
'utf-8') for ele in (ctypes.cast(data, ctypes.POINTER(ctypes.c_char * nbytes)))[:abs(num_of_rows)]]
else: else:
return [ None if ele.value[0:1] == FieldType.C_BINARY_NULL else ele.value.decode('utf-8') for ele in (ctypes.cast(data, ctypes.POINTER(ctypes.c_char * nbytes)))[:abs(num_of_rows)]] return [None if ele.value[0:1] == FieldType.C_BINARY_NULL else ele.value.decode(
'utf-8') for ele in (ctypes.cast(data, ctypes.POINTER(ctypes.c_char * nbytes)))[:abs(num_of_rows)]]
def _crow_nchar_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_nchar_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C nchar row to python row """Function to convert C nchar row to python row
""" """
assert(nbytes is not None) assert(nbytes is not None)
res=[] res = []
for i in range(abs(num_of_rows)): for i in range(abs(num_of_rows)):
try: try:
if num_of_rows >= 0: if num_of_rows >= 0:
tmpstr = ctypes.c_char_p(data) tmpstr = ctypes.c_char_p(data)
res.append( tmpstr.value.decode() ) res.append(tmpstr.value.decode())
else: else:
res.append( (ctypes.cast(data+nbytes*i, ctypes.POINTER(ctypes.c_wchar * (nbytes//4))))[0].value ) res.append((ctypes.cast(data + nbytes * i,
ctypes.POINTER(ctypes.c_wchar * (nbytes // 4))))[0].value)
except ValueError: except ValueError:
res.append(None) res.append(None)
return res return res
def _crow_binary_to_python_block(data, num_of_rows, nbytes=None, micro=False): def _crow_binary_to_python_block(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C binary row to python row """Function to convert C binary row to python row
""" """
assert(nbytes is not None) assert(nbytes is not None)
res=[] res = []
if num_of_rows > 0: if num_of_rows > 0:
for i in range(abs(num_of_rows)): for i in range(abs(num_of_rows)):
try: try:
rbyte=ctypes.cast(data+nbytes*i,ctypes.POINTER(ctypes.c_short))[:1].pop() rbyte = ctypes.cast(
tmpstr = ctypes.c_char_p(data+nbytes*i+2) data + nbytes * i,
res.append( tmpstr.value.decode()[0:rbyte] ) ctypes.POINTER(
ctypes.c_short))[
:1].pop()
tmpstr = ctypes.c_char_p(data + nbytes * i + 2)
res.append(tmpstr.value.decode()[0:rbyte])
except ValueError: except ValueError:
res.append(None) res.append(None)
else: else:
for i in range(abs(num_of_rows)): for i in range(abs(num_of_rows)):
try: try:
rbyte=ctypes.cast(data+nbytes*i,ctypes.POINTER(ctypes.c_short))[:1].pop() rbyte = ctypes.cast(
tmpstr = ctypes.c_char_p(data+nbytes*i+2) data + nbytes * i,
res.append( tmpstr.value.decode()[0:rbyte] ) ctypes.POINTER(
ctypes.c_short))[
:1].pop()
tmpstr = ctypes.c_char_p(data + nbytes * i + 2)
res.append(tmpstr.value.decode()[0:rbyte])
except ValueError: except ValueError:
res.append(None) res.append(None)
return res return res
def _crow_nchar_to_python_block(data, num_of_rows, nbytes=None, micro=False): def _crow_nchar_to_python_block(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C nchar row to python row """Function to convert C nchar row to python row
""" """
assert(nbytes is not None) assert(nbytes is not None)
res=[] res = []
if num_of_rows >= 0: if num_of_rows >= 0:
for i in range(abs(num_of_rows)): for i in range(abs(num_of_rows)):
try: try:
tmpstr = ctypes.c_char_p(data+nbytes*i+2) tmpstr = ctypes.c_char_p(data + nbytes * i + 2)
res.append( tmpstr.value.decode() ) res.append(tmpstr.value.decode())
except ValueError: except ValueError:
res.append(None) res.append(None)
else: else:
for i in range(abs(num_of_rows)): for i in range(abs(num_of_rows)):
try: try:
res.append( (ctypes.cast(data+nbytes*i+2, ctypes.POINTER(ctypes.c_wchar * (nbytes//4))))[0].value ) res.append((ctypes.cast(data + nbytes * i + 2,
ctypes.POINTER(ctypes.c_wchar * (nbytes // 4))))[0].value)
except ValueError: except ValueError:
res.append(None) res.append(None)
return res return res
_CONVERT_FUNC = { _CONVERT_FUNC = {
FieldType.C_BOOL: _crow_bool_to_python, FieldType.C_BOOL: _crow_bool_to_python,
FieldType.C_TINYINT : _crow_tinyint_to_python, FieldType.C_TINYINT: _crow_tinyint_to_python,
FieldType.C_SMALLINT : _crow_smallint_to_python, FieldType.C_SMALLINT: _crow_smallint_to_python,
FieldType.C_INT : _crow_int_to_python, FieldType.C_INT: _crow_int_to_python,
FieldType.C_BIGINT : _crow_bigint_to_python, FieldType.C_BIGINT: _crow_bigint_to_python,
FieldType.C_FLOAT : _crow_float_to_python, FieldType.C_FLOAT: _crow_float_to_python,
FieldType.C_DOUBLE : _crow_double_to_python, FieldType.C_DOUBLE: _crow_double_to_python,
FieldType.C_BINARY: _crow_binary_to_python, FieldType.C_BINARY: _crow_binary_to_python,
FieldType.C_TIMESTAMP : _crow_timestamp_to_python, FieldType.C_TIMESTAMP: _crow_timestamp_to_python,
FieldType.C_NCHAR : _crow_nchar_to_python FieldType.C_NCHAR: _crow_nchar_to_python,
FieldType.C_TINYINT_UNSIGNED: _crow_tinyint_unsigned_to_python,
FieldType.C_SMALLINT_UNSIGNED: _crow_smallint_unsigned_to_python,
FieldType.C_INT_UNSIGNED: _crow_int_unsigned_to_python,
FieldType.C_BIGINT_UNSIGNED: _crow_bigint_unsigned_to_python
} }
_CONVERT_FUNC_BLOCK = { _CONVERT_FUNC_BLOCK = {
FieldType.C_BOOL: _crow_bool_to_python, FieldType.C_BOOL: _crow_bool_to_python,
FieldType.C_TINYINT : _crow_tinyint_to_python, FieldType.C_TINYINT: _crow_tinyint_to_python,
FieldType.C_SMALLINT : _crow_smallint_to_python, FieldType.C_SMALLINT: _crow_smallint_to_python,
FieldType.C_INT : _crow_int_to_python, FieldType.C_INT: _crow_int_to_python,
FieldType.C_BIGINT : _crow_bigint_to_python, FieldType.C_BIGINT: _crow_bigint_to_python,
FieldType.C_FLOAT : _crow_float_to_python, FieldType.C_FLOAT: _crow_float_to_python,
FieldType.C_DOUBLE : _crow_double_to_python, FieldType.C_DOUBLE: _crow_double_to_python,
FieldType.C_BINARY: _crow_binary_to_python_block, FieldType.C_BINARY: _crow_binary_to_python_block,
FieldType.C_TIMESTAMP : _crow_timestamp_to_python, FieldType.C_TIMESTAMP: _crow_timestamp_to_python,
FieldType.C_NCHAR : _crow_nchar_to_python_block FieldType.C_NCHAR: _crow_nchar_to_python_block,
FieldType.C_TINYINT_UNSIGNED: _crow_tinyint_unsigned_to_python,
FieldType.C_SMALLINT_UNSIGNED: _crow_smallint_unsigned_to_python,
FieldType.C_INT_UNSIGNED: _crow_int_unsigned_to_python,
FieldType.C_BIGINT_UNSIGNED: _crow_bigint_unsigned_to_python
} }
# Corresponding TAOS_FIELD structure in C # Corresponding TAOS_FIELD structure in C
class TaosField(ctypes.Structure): class TaosField(ctypes.Structure):
_fields_ = [('name', ctypes.c_char * 65), _fields_ = [('name', ctypes.c_char * 65),
('type', ctypes.c_char), ('type', ctypes.c_char),
('bytes', ctypes.c_short)] ('bytes', ctypes.c_short)]
# C interface class # C interface class
class CTaosInterface(object): class CTaosInterface(object):
libtaos = ctypes.CDLL('libtaos.so') libtaos = ctypes.CDLL('libtaos.so')
@ -216,7 +360,7 @@ class CTaosInterface(object):
except AttributeError: except AttributeError:
raise AttributeError("config is expected as a str") raise AttributeError("config is expected as a str")
if config != None: if config is not None:
CTaosInterface.libtaos.taos_options(3, self._config) CTaosInterface.libtaos.taos_options(3, self._config)
CTaosInterface.libtaos.taos_init() CTaosInterface.libtaos.taos_init()
@ -227,7 +371,13 @@ class CTaosInterface(object):
""" """
return self._config return self._config
def connect(self, host=None, user="root", password="taosdata", db=None, port=0): def connect(
self,
host=None,
user="root",
password="taosdata",
db=None,
port=0):
''' '''
Function to connect to server Function to connect to server
@ -236,7 +386,7 @@ class CTaosInterface(object):
# host # host
try: try:
_host = ctypes.c_char_p(host.encode( _host = ctypes.c_char_p(host.encode(
"utf-8")) if host != None else ctypes.c_char_p(None) "utf-8")) if host is not None else ctypes.c_char_p(None)
except AttributeError: except AttributeError:
raise AttributeError("host is expected as a str") raise AttributeError("host is expected as a str")
@ -255,7 +405,7 @@ class CTaosInterface(object):
# db # db
try: try:
_db = ctypes.c_char_p( _db = ctypes.c_char_p(
db.encode("utf-8")) if db != None else ctypes.c_char_p(None) db.encode("utf-8")) if db is not None else ctypes.c_char_p(None)
except AttributeError: except AttributeError:
raise AttributeError("db is expected as a str") raise AttributeError("db is expected as a str")
@ -268,11 +418,11 @@ class CTaosInterface(object):
connection = ctypes.c_void_p(CTaosInterface.libtaos.taos_connect( connection = ctypes.c_void_p(CTaosInterface.libtaos.taos_connect(
_host, _user, _password, _db, _port)) _host, _user, _password, _db, _port))
if connection.value == None: if connection.value is None:
print('connect to TDengine failed') print('connect to TDengine failed')
raise ConnectionError("connect to TDengine failed") raise ConnectionError("connect to TDengine failed")
# sys.exit(1) # sys.exit(1)
#else: # else:
# print('connect to TDengine success') # print('connect to TDengine success')
return connection return connection
@ -293,12 +443,13 @@ class CTaosInterface(object):
@rtype: 0 on success and -1 on failure @rtype: 0 on success and -1 on failure
''' '''
try: try:
return CTaosInterface.libtaos.taos_query(connection, ctypes.c_char_p(sql.encode('utf-8'))) return CTaosInterface.libtaos.taos_query(
connection, ctypes.c_char_p(sql.encode('utf-8')))
except AttributeError: except AttributeError:
raise AttributeError("sql is expected as a string") raise AttributeError("sql is expected as a string")
# finally: # finally:
# CTaosInterface.libtaos.close(connection) # CTaosInterface.libtaos.close(connection)
@staticmethod @staticmethod
def affectedRows(result): def affectedRows(result):
"""The affected rows after runing query """The affected rows after runing query
@ -308,7 +459,7 @@ class CTaosInterface(object):
@staticmethod @staticmethod
def subscribe(connection, restart, topic, sql, interval): def subscribe(connection, restart, topic, sql, interval):
"""Create a subscription """Create a subscription
@restart boolean, @restart boolean,
@sql string, sql statement for data query, must be a 'select' statement. @sql string, sql statement for data query, must be a 'select' statement.
@topic string, name of this subscription @topic string, name of this subscription
""" """
@ -360,38 +511,53 @@ class CTaosInterface(object):
result, ctypes.byref(pblock)) result, ctypes.byref(pblock))
if num_of_rows == 0: if num_of_rows == 0:
return None, 0 return None, 0
isMicro = (CTaosInterface.libtaos.taos_result_precision(result) == FieldType.C_TIMESTAMP_MICRO) isMicro = (CTaosInterface.libtaos.taos_result_precision(
result) == FieldType.C_TIMESTAMP_MICRO)
blocks = [None] * len(fields) blocks = [None] * len(fields)
fieldL = CTaosInterface.libtaos.taos_fetch_lengths(result) fieldL = CTaosInterface.libtaos.taos_fetch_lengths(result)
fieldLen = [ele for ele in ctypes.cast(fieldL, ctypes.POINTER(ctypes.c_int))[:len(fields)]] fieldLen = [
ele for ele in ctypes.cast(
fieldL, ctypes.POINTER(
ctypes.c_int))[
:len(fields)]]
for i in range(len(fields)): for i in range(len(fields)):
data = ctypes.cast(pblock, ctypes.POINTER(ctypes.c_void_p))[i] data = ctypes.cast(pblock, ctypes.POINTER(ctypes.c_void_p))[i]
if fields[i]['type'] not in _CONVERT_FUNC_BLOCK: if fields[i]['type'] not in _CONVERT_FUNC_BLOCK:
raise DatabaseError("Invalid data type returned from database") raise DatabaseError("Invalid data type returned from database")
blocks[i] = _CONVERT_FUNC_BLOCK[fields[i]['type']](data, num_of_rows, fieldLen[i], isMicro) blocks[i] = _CONVERT_FUNC_BLOCK[fields[i]['type']](
data, num_of_rows, fieldLen[i], isMicro)
return blocks, abs(num_of_rows) return blocks, abs(num_of_rows)
@staticmethod @staticmethod
def fetchRow(result, fields): def fetchRow(result, fields):
pblock = ctypes.c_void_p(0) pblock = ctypes.c_void_p(0)
pblock = CTaosInterface.libtaos.taos_fetch_row(result) pblock = CTaosInterface.libtaos.taos_fetch_row(result)
if pblock : if pblock:
num_of_rows = 1 num_of_rows = 1
isMicro = (CTaosInterface.libtaos.taos_result_precision(result) == FieldType.C_TIMESTAMP_MICRO) isMicro = (CTaosInterface.libtaos.taos_result_precision(
result) == FieldType.C_TIMESTAMP_MICRO)
blocks = [None] * len(fields) blocks = [None] * len(fields)
fieldL = CTaosInterface.libtaos.taos_fetch_lengths(result) fieldL = CTaosInterface.libtaos.taos_fetch_lengths(result)
fieldLen = [ele for ele in ctypes.cast(fieldL, ctypes.POINTER(ctypes.c_int))[:len(fields)]] fieldLen = [
ele for ele in ctypes.cast(
fieldL, ctypes.POINTER(
ctypes.c_int))[
:len(fields)]]
for i in range(len(fields)): for i in range(len(fields)):
data = ctypes.cast(pblock, ctypes.POINTER(ctypes.c_void_p))[i] data = ctypes.cast(pblock, ctypes.POINTER(ctypes.c_void_p))[i]
if fields[i]['type'] not in _CONVERT_FUNC: if fields[i]['type'] not in _CONVERT_FUNC:
raise DatabaseError("Invalid data type returned from database") raise DatabaseError(
"Invalid data type returned from database")
if data is None: if data is None:
blocks[i] = [None] blocks[i] = [None]
else: else:
blocks[i] = _CONVERT_FUNC[fields[i]['type']](data, num_of_rows, fieldLen[i], isMicro) blocks[i] = _CONVERT_FUNC[fields[i]['type']](
data, num_of_rows, fieldLen[i], isMicro)
else: else:
return None, 0 return None, 0
return blocks, abs(num_of_rows) return blocks, abs(num_of_rows)
@staticmethod @staticmethod
def freeResult(result): def freeResult(result):
CTaosInterface.libtaos.taos_free_result(result) CTaosInterface.libtaos.taos_free_result(result)

View File

@ -2,9 +2,11 @@ from .cursor import TDengineCursor
from .subscription import TDengineSubscription from .subscription import TDengineSubscription
from .cinterface import CTaosInterface from .cinterface import CTaosInterface
class TDengineConnection(object): class TDengineConnection(object):
""" TDengine connection object """ TDengine connection object
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self._conn = None self._conn = None
self._host = None self._host = None
@ -29,7 +31,7 @@ class TDengineConnection(object):
# password # password
if 'password' in kwargs: if 'password' in kwargs:
self._password = kwargs['password'] self._password = kwargs['password']
# database # database
if 'database' in kwargs: if 'database' in kwargs:
self._database = kwargs['database'] self._database = kwargs['database']
@ -43,7 +45,12 @@ class TDengineConnection(object):
self._config = kwargs['config'] self._config = kwargs['config']
self._chandle = CTaosInterface(self._config) self._chandle = CTaosInterface(self._config)
self._conn = self._chandle.connect(self._host, self._user, self._password, self._database, self._port) self._conn = self._chandle.connect(
self._host,
self._user,
self._password,
self._database,
self._port)
def close(self): def close(self):
"""Close current connection. """Close current connection.
@ -55,7 +62,8 @@ class TDengineConnection(object):
""" """
if self._conn is None: if self._conn is None:
return None return None
sub = CTaosInterface.subscribe(self._conn, restart, topic, sql, interval) sub = CTaosInterface.subscribe(
self._conn, restart, topic, sql, interval)
return TDengineSubscription(sub) return TDengineSubscription(sub)
def cursor(self): def cursor(self):
@ -80,7 +88,8 @@ class TDengineConnection(object):
""" """
pass pass
if __name__ == "__main__": if __name__ == "__main__":
conn = TDengineConnection(host='192.168.1.107') conn = TDengineConnection(host='192.168.1.107')
conn.close() conn.close()
print("Hello world") print("Hello world")

View File

@ -3,6 +3,7 @@
from .dbapi import * from .dbapi import *
class FieldType(object): class FieldType(object):
"""TDengine Field Types """TDengine Field Types
""" """
@ -18,13 +19,21 @@ class FieldType(object):
C_BINARY = 8 C_BINARY = 8
C_TIMESTAMP = 9 C_TIMESTAMP = 9
C_NCHAR = 10 C_NCHAR = 10
C_TINYINT_UNSIGNED = 12
C_SMALLINT_UNSIGNED = 13
C_INT_UNSIGNED = 14
C_BIGINT_UNSIGNED = 15
# NULL value definition # NULL value definition
# NOTE: These values should change according to C definition in tsdb.h # NOTE: These values should change according to C definition in tsdb.h
C_BOOL_NULL = 0x02 C_BOOL_NULL = 0x02
C_TINYINT_NULL = -128 C_TINYINT_NULL = -128
C_TINYINT_UNSIGNED_NULL = 255
C_SMALLINT_NULL = -32768 C_SMALLINT_NULL = -32768
C_SMALLINT_UNSIGNED_NULL = 65535
C_INT_NULL = -2147483648 C_INT_NULL = -2147483648
C_INT_UNSIGNED_NULL = 4294967295
C_BIGINT_NULL = -9223372036854775808 C_BIGINT_NULL = -9223372036854775808
C_BIGINT_UNSIGNED_NULL = 18446744073709551615
C_FLOAT_NULL = float('nan') C_FLOAT_NULL = float('nan')
C_DOUBLE_NULL = float('nan') C_DOUBLE_NULL = float('nan')
C_BINARY_NULL = bytearray([int('0xff', 16)]) C_BINARY_NULL = bytearray([int('0xff', 16)])

View File

@ -128,8 +128,8 @@ class TDengineCursor(object):
if errno == 0: if errno == 0:
if CTaosInterface.fieldsCount(self._result) == 0: if CTaosInterface.fieldsCount(self._result) == 0:
self._affected_rows += CTaosInterface.affectedRows( self._affected_rows += CTaosInterface.affectedRows(
self._result ) self._result)
return CTaosInterface.affectedRows(self._result ) return CTaosInterface.affectedRows(self._result)
else: else:
self._fields = CTaosInterface.useResult( self._fields = CTaosInterface.useResult(
self._result) self._result)
@ -148,6 +148,7 @@ class TDengineCursor(object):
"""Fetch the next row of a query result set, returning a single sequence, or None when no more data is available. """Fetch the next row of a query result set, returning a single sequence, or None when no more data is available.
""" """
pass pass
def fetchmany(self): def fetchmany(self):
pass pass
@ -158,11 +159,26 @@ class TDengineCursor(object):
if (dataType.upper() == "TINYINT"): if (dataType.upper() == "TINYINT"):
if (self._description[col][1] == FieldType.C_TINYINT): if (self._description[col][1] == FieldType.C_TINYINT):
return True return True
if (dataType.upper() == "TINYINT UNSIGNED"):
if (self._description[col][1] == FieldType.C_TINYINT_UNSIGNED):
return True
if (dataType.upper() == "SMALLINT"):
if (self._description[col][1] == FieldType.C_SMALLINT):
return True
if (dataType.upper() == "SMALLINT UNSIGNED"):
if (self._description[col][1] == FieldType.C_SMALLINT_UNSIGNED):
return True
if (dataType.upper() == "INT"): if (dataType.upper() == "INT"):
if (self._description[col][1] == FieldType.C_INT): if (self._description[col][1] == FieldType.C_INT):
return True return True
if (dataType.upper() == "INT UNSIGNED"):
if (self._description[col][1] == FieldType.C_INT_UNSIGNED):
return True
if (dataType.upper() == "BIGINT"): if (dataType.upper() == "BIGINT"):
if (self._description[col][1] == FieldType.C_INT): if (self._description[col][1] == FieldType.C_BIGINT):
return True
if (dataType.upper() == "BIGINT UNSIGNED"):
if (self._description[col][1] == FieldType.C_BIGINT_UNSIGNED):
return True return True
if (dataType.upper() == "FLOAT"): if (dataType.upper() == "FLOAT"):
if (self._description[col][1] == FieldType.C_FLOAT): if (self._description[col][1] == FieldType.C_FLOAT):
@ -191,16 +207,20 @@ class TDengineCursor(object):
buffer = [[] for i in range(len(self._fields))] buffer = [[] for i in range(len(self._fields))]
self._rowcount = 0 self._rowcount = 0
while True: while True:
block, num_of_fields = CTaosInterface.fetchRow(self._result, self._fields) block, num_of_fields = CTaosInterface.fetchRow(
self._result, self._fields)
errno = CTaosInterface.libtaos.taos_errno(self._result) errno = CTaosInterface.libtaos.taos_errno(self._result)
if errno != 0: if errno != 0:
raise ProgrammingError(CTaosInterface.errStr(self._result), errno) raise ProgrammingError(
CTaosInterface.errStr(
self._result), errno)
if num_of_fields == 0: if num_of_fields == 0:
break break
self._rowcount += num_of_fields self._rowcount += num_of_fields
for i in range(len(self._fields)): for i in range(len(self._fields)):
buffer[i].extend(block[i]) buffer[i].extend(block[i])
return list(map(tuple, zip(*buffer))) return list(map(tuple, zip(*buffer)))
def fetchall(self): def fetchall(self):
if self._result is None or self._fields is None: if self._result is None or self._fields is None:
raise OperationalError("Invalid use of fetchall") raise OperationalError("Invalid use of fetchall")
@ -208,16 +228,20 @@ class TDengineCursor(object):
buffer = [[] for i in range(len(self._fields))] buffer = [[] for i in range(len(self._fields))]
self._rowcount = 0 self._rowcount = 0
while True: while True:
block, num_of_fields = CTaosInterface.fetchBlock(self._result, self._fields) block, num_of_fields = CTaosInterface.fetchBlock(
self._result, self._fields)
errno = CTaosInterface.libtaos.taos_errno(self._result) errno = CTaosInterface.libtaos.taos_errno(self._result)
if errno != 0: if errno != 0:
raise ProgrammingError(CTaosInterface.errStr(self._result), errno) raise ProgrammingError(
CTaosInterface.errStr(
self._result), errno)
if num_of_fields == 0: if num_of_fields == 0:
break break
self._rowcount += num_of_fields self._rowcount += num_of_fields
for i in range(len(self._fields)): for i in range(len(self._fields)):
buffer[i].extend(block[i]) buffer[i].extend(block[i])
return list(map(tuple, zip(*buffer))) return list(map(tuple, zip(*buffer)))
def nextset(self): def nextset(self):
""" """
""" """

View File

@ -4,6 +4,7 @@
import time import time
import datetime import datetime
class DBAPITypeObject(object): class DBAPITypeObject(object):
def __init__(self, *values): def __init__(self, *values):
self.values = values self.values = values
@ -16,23 +17,28 @@ class DBAPITypeObject(object):
else: else:
return -1 return -1
Date = datetime.date Date = datetime.date
Time = datetime.time Time = datetime.time
Timestamp = datetime.datetime Timestamp = datetime.datetime
def DataFromTicks(ticks): def DataFromTicks(ticks):
return Date(*time.localtime(ticks)[:3]) return Date(*time.localtime(ticks)[:3])
def TimeFromTicks(ticks): def TimeFromTicks(ticks):
return Time(*time.localtime(ticks)[3:6]) return Time(*time.localtime(ticks)[3:6])
def TimestampFromTicks(ticks): def TimestampFromTicks(ticks):
return Timestamp(*time.localtime(ticks)[:6]) return Timestamp(*time.localtime(ticks)[:6])
Binary = bytes Binary = bytes
# STRING = DBAPITypeObject(*constants.FieldType.get_string_types()) # STRING = DBAPITypeObject(*constants.FieldType.get_string_types())
# BINARY = DBAPITypeObject(*constants.FieldType.get_binary_types()) # BINARY = DBAPITypeObject(*constants.FieldType.get_binary_types())
# NUMBER = BAPITypeObject(*constants.FieldType.get_number_types()) # NUMBER = BAPITypeObject(*constants.FieldType.get_number_types())
# DATETIME = DBAPITypeObject(*constants.FieldType.get_timestamp_types()) # DATETIME = DBAPITypeObject(*constants.FieldType.get_timestamp_types())
# ROWID = DBAPITypeObject() # ROWID = DBAPITypeObject()

View File

@ -1,35 +1,41 @@
"""Python exceptions """Python exceptions
""" """
class Error(Exception): class Error(Exception):
def __init__(self, msg=None, errno=None): def __init__(self, msg=None, errno=None):
self.msg = msg self.msg = msg
self._full_msg = self.msg self._full_msg = self.msg
self.errno = errno self.errno = errno
def __str__(self): def __str__(self):
return self._full_msg return self._full_msg
class Warning(Exception): class Warning(Exception):
"""Exception raised for important warnings like data truncations while inserting. """Exception raised for important warnings like data truncations while inserting.
""" """
pass pass
class InterfaceError(Error): class InterfaceError(Error):
"""Exception raised for errors that are related to the database interface rather than the database itself. """Exception raised for errors that are related to the database interface rather than the database itself.
""" """
pass pass
class DatabaseError(Error): class DatabaseError(Error):
"""Exception raised for errors that are related to the database. """Exception raised for errors that are related to the database.
""" """
pass pass
class DataError(DatabaseError): class DataError(DatabaseError):
"""Exception raised for errors that are due to problems with the processed data like division by zero, numeric value out of range. """Exception raised for errors that are due to problems with the processed data like division by zero, numeric value out of range.
""" """
pass pass
class OperationalError(DatabaseError): class OperationalError(DatabaseError):
"""Exception raised for errors that are related to the database's operation and not necessarily under the control of the programmer """Exception raised for errors that are related to the database's operation and not necessarily under the control of the programmer
""" """
@ -41,17 +47,20 @@ class IntegrityError(DatabaseError):
""" """
pass pass
class InternalError(DatabaseError): class InternalError(DatabaseError):
"""Exception raised when the database encounters an internal error. """Exception raised when the database encounters an internal error.
""" """
pass pass
class ProgrammingError(DatabaseError): class ProgrammingError(DatabaseError):
"""Exception raised for programming errors. """Exception raised for programming errors.
""" """
pass pass
class NotSupportedError(DatabaseError): class NotSupportedError(DatabaseError):
"""Exception raised in case a method or database API was used which is not supported by the database,. """Exception raised in case a method or database API was used which is not supported by the database,.
""" """
pass pass

View File

@ -1,52 +1,57 @@
from .cinterface import CTaosInterface from .cinterface import CTaosInterface
from .error import * from .error import *
class TDengineSubscription(object): class TDengineSubscription(object):
"""TDengine subscription object """TDengine subscription object
""" """
def __init__(self, sub): def __init__(self, sub):
self._sub = sub self._sub = sub
def consume(self): def consume(self):
"""Consume rows of a subscription """Consume rows of a subscription
""" """
if self._sub is None: if self._sub is None:
raise OperationalError("Invalid use of consume") raise OperationalError("Invalid use of consume")
result, fields = CTaosInterface.consume(self._sub) result, fields = CTaosInterface.consume(self._sub)
buffer = [[] for i in range(len(fields))] buffer = [[] for i in range(len(fields))]
while True: while True:
block, num_of_fields = CTaosInterface.fetchBlock(result, fields) block, num_of_fields = CTaosInterface.fetchBlock(result, fields)
if num_of_fields == 0: break if num_of_fields == 0:
break
for i in range(len(fields)): for i in range(len(fields)):
buffer[i].extend(block[i]) buffer[i].extend(block[i])
self.fields = fields self.fields = fields
return list(map(tuple, zip(*buffer))) return list(map(tuple, zip(*buffer)))
def close(self, keepProgress=True):
def close(self, keepProgress = True):
"""Close the Subscription. """Close the Subscription.
""" """
if self._sub is None: if self._sub is None:
return False return False
CTaosInterface.unsubscribe(self._sub, keepProgress) CTaosInterface.unsubscribe(self._sub, keepProgress)
return True return True
if __name__ == '__main__': if __name__ == '__main__':
from .connection import TDengineConnection from .connection import TDengineConnection
conn = TDengineConnection(host="127.0.0.1", user="root", password="taosdata", database="test") conn = TDengineConnection(
host="127.0.0.1",
user="root",
password="taosdata",
database="test")
# Generate a cursor object to run SQL commands # Generate a cursor object to run SQL commands
sub = conn.subscribe(True, "test", "select * from meters;", 1000) sub = conn.subscribe(True, "test", "select * from meters;", 1000)
for i in range(0,10): for i in range(0, 10):
data = sub.consume() data = sub.consume()
for d in data: for d in data:
print(d) print(d)
sub.close() sub.close()
conn.close() conn.close()

View File

@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
setuptools.setup( setuptools.setup(
name="taos", name="taos",
version="2.0.4", version="2.0.5",
author="Taosdata Inc.", author="Taosdata Inc.",
author_email="support@taosdata.com", author_email="support@taosdata.com",
description="TDengine python client package", description="TDengine python client package",

View File

@ -3,12 +3,12 @@ from .connection import TDengineConnection
from .cursor import TDengineCursor from .cursor import TDengineCursor
# Globals # Globals
apilevel = '2.0.3'
threadsafety = 0 threadsafety = 0
paramstyle = 'pyformat' paramstyle = 'pyformat'
__all__ = ['connection', 'cursor'] __all__ = ['connection', 'cursor']
def connect(*args, **kwargs): def connect(*args, **kwargs):
""" Function to return a TDengine connector object """ Function to return a TDengine connector object

View File

@ -4,11 +4,14 @@ from .error import *
import math import math
import datetime import datetime
def _convert_millisecond_to_datetime(milli): def _convert_millisecond_to_datetime(milli):
return datetime.datetime.fromtimestamp(milli/1000.0) return datetime.datetime.fromtimestamp(milli / 1000.0)
def _convert_microsecond_to_datetime(micro): def _convert_microsecond_to_datetime(micro):
return datetime.datetime.fromtimestamp(micro/1000000.0) return datetime.datetime.fromtimestamp(micro / 1000000.0)
def _crow_timestamp_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_timestamp_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C bool row to python row """Function to convert C bool row to python row
@ -18,168 +21,309 @@ def _crow_timestamp_to_python(data, num_of_rows, nbytes=None, micro=False):
_timestamp_converter = _convert_microsecond_to_datetime _timestamp_converter = _convert_microsecond_to_datetime
if num_of_rows > 0: if num_of_rows > 0:
return list(map(_timestamp_converter, ctypes.cast(data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)])) return list(map(_timestamp_converter, ctypes.cast(
data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)]))
else: else:
return list(map(_timestamp_converter, ctypes.cast(data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)])) return list(map(_timestamp_converter, ctypes.cast(
data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)]))
def _crow_bool_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_bool_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C bool row to python row """Function to convert C bool row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele == FieldType.C_BOOL_NULL else bool(ele) for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_byte))[:abs(num_of_rows)] ] return [
None if ele == FieldType.C_BOOL_NULL else bool(ele) for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_byte))[
:abs(num_of_rows)]]
else: else:
return [ None if ele == FieldType.C_BOOL_NULL else bool(ele) for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_bool))[:abs(num_of_rows)] ] return [
None if ele == FieldType.C_BOOL_NULL else bool(ele) for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_bool))[
:abs(num_of_rows)]]
def _crow_tinyint_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_tinyint_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C tinyint row to python row """Function to convert C tinyint row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele == FieldType.C_TINYINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_byte))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_TINYINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_byte))[:abs(num_of_rows)]]
else: else:
return [ None if ele == FieldType.C_TINYINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_byte))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_TINYINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_byte))[:abs(num_of_rows)]]
def _crow_tinyint_unsigned_to_python(
data,
num_of_rows,
nbytes=None,
micro=False):
"""Function to convert C tinyint row to python row
"""
if num_of_rows > 0:
return [
None if ele == FieldType.C_TINYINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_byte))[
:abs(num_of_rows)]]
else:
return [
None if ele == FieldType.C_TINYINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_byte))[
:abs(num_of_rows)]]
def _crow_smallint_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_smallint_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C smallint row to python row """Function to convert C smallint row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele == FieldType.C_SMALLINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_short))[:abs(num_of_rows)]] return [
None if ele == FieldType.C_SMALLINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_short))[
:abs(num_of_rows)]]
else: else:
return [ None if ele == FieldType.C_SMALLINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_short))[:abs(num_of_rows)] ] return [
None if ele == FieldType.C_SMALLINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_short))[
:abs(num_of_rows)]]
def _crow_smallint_unsigned_to_python(
data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C smallint row to python row
"""
if num_of_rows > 0:
return [
None if ele == FieldType.C_SMALLINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_short))[
:abs(num_of_rows)]]
else:
return [
None if ele == FieldType.C_SMALLINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_short))[
:abs(num_of_rows)]]
def _crow_int_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_int_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C int row to python row """Function to convert C int row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele == FieldType.C_INT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_int))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_INT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_int))[:abs(num_of_rows)]]
else: else:
return [ None if ele == FieldType.C_INT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_int))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_INT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_int))[:abs(num_of_rows)]]
def _crow_int_unsigned_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C int row to python row
"""
if num_of_rows > 0:
return [
None if ele == FieldType.C_INT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_int))[
:abs(num_of_rows)]]
else:
return [
None if ele == FieldType.C_INT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_int))[
:abs(num_of_rows)]]
def _crow_bigint_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_bigint_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C bigint row to python row """Function to convert C bigint row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele == FieldType.C_BIGINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_BIGINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)]]
else: else:
return [ None if ele == FieldType.C_BIGINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_BIGINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)]]
def _crow_bigint_unsigned_to_python(
data,
num_of_rows,
nbytes=None,
micro=False):
"""Function to convert C bigint row to python row
"""
if num_of_rows > 0:
return [
None if ele == FieldType.C_BIGINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_long))[
:abs(num_of_rows)]]
else:
return [
None if ele == FieldType.C_BIGINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_long))[
:abs(num_of_rows)]]
def _crow_float_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_float_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C float row to python row """Function to convert C float row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if math.isnan(ele) else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_float))[:abs(num_of_rows)] ] return [None if math.isnan(ele) else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_float))[:abs(num_of_rows)]]
else: else:
return [ None if math.isnan(ele) else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_float))[:abs(num_of_rows)] ] return [None if math.isnan(ele) else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_float))[:abs(num_of_rows)]]
def _crow_double_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_double_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C double row to python row """Function to convert C double row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if math.isnan(ele) else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_double))[:abs(num_of_rows)] ] return [None if math.isnan(ele) else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_double))[:abs(num_of_rows)]]
else: else:
return [ None if math.isnan(ele) else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_double))[:abs(num_of_rows)] ] return [None if math.isnan(ele) else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_double))[:abs(num_of_rows)]]
def _crow_binary_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_binary_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C binary row to python row """Function to convert C binary row to python row
""" """
assert(nbytes is not None) assert(nbytes is not None)
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele.value[0:1] == FieldType.C_BINARY_NULL else ele.value.decode('utf-8') for ele in (ctypes.cast(data, ctypes.POINTER(ctypes.c_char * nbytes)))[:abs(num_of_rows)]] return [None if ele.value[0:1] == FieldType.C_BINARY_NULL else ele.value.decode(
'utf-8') for ele in (ctypes.cast(data, ctypes.POINTER(ctypes.c_char * nbytes)))[:abs(num_of_rows)]]
else: else:
return [ None if ele.value[0:1] == FieldType.C_BINARY_NULL else ele.value.decode('utf-8') for ele in (ctypes.cast(data, ctypes.POINTER(ctypes.c_char * nbytes)))[:abs(num_of_rows)]] return [None if ele.value[0:1] == FieldType.C_BINARY_NULL else ele.value.decode(
'utf-8') for ele in (ctypes.cast(data, ctypes.POINTER(ctypes.c_char * nbytes)))[:abs(num_of_rows)]]
def _crow_nchar_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_nchar_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C nchar row to python row """Function to convert C nchar row to python row
""" """
assert(nbytes is not None) assert(nbytes is not None)
res=[] res = []
for i in range(abs(num_of_rows)): for i in range(abs(num_of_rows)):
try: try:
if num_of_rows >= 0: if num_of_rows >= 0:
tmpstr = ctypes.c_char_p(data) tmpstr = ctypes.c_char_p(data)
res.append( tmpstr.value.decode() ) res.append(tmpstr.value.decode())
else: else:
res.append( (ctypes.cast(data+nbytes*i, ctypes.POINTER(ctypes.c_wchar * (nbytes//4))))[0].value ) res.append((ctypes.cast(data + nbytes * i,
ctypes.POINTER(ctypes.c_wchar * (nbytes // 4))))[0].value)
except ValueError: except ValueError:
res.append(None) res.append(None)
return res return res
def _crow_binary_to_python_block(data, num_of_rows, nbytes=None, micro=False): def _crow_binary_to_python_block(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C binary row to python row """Function to convert C binary row to python row
""" """
assert(nbytes is not None) assert(nbytes is not None)
res=[] res = []
if num_of_rows > 0: if num_of_rows > 0:
for i in range(abs(num_of_rows)): for i in range(abs(num_of_rows)):
try: try:
rbyte=ctypes.cast(data+nbytes*i,ctypes.POINTER(ctypes.c_short))[:1].pop() rbyte = ctypes.cast(
tmpstr = ctypes.c_char_p(data+nbytes*i+2) data + nbytes * i,
res.append( tmpstr.value.decode()[0:rbyte] ) ctypes.POINTER(
ctypes.c_short))[
:1].pop()
tmpstr = ctypes.c_char_p(data + nbytes * i + 2)
res.append(tmpstr.value.decode()[0:rbyte])
except ValueError: except ValueError:
res.append(None) res.append(None)
else: else:
for i in range(abs(num_of_rows)): for i in range(abs(num_of_rows)):
try: try:
rbyte=ctypes.cast(data+nbytes*i,ctypes.POINTER(ctypes.c_short))[:1].pop() rbyte = ctypes.cast(
tmpstr = ctypes.c_char_p(data+nbytes*i+2) data + nbytes * i,
res.append( tmpstr.value.decode()[0:rbyte] ) ctypes.POINTER(
ctypes.c_short))[
:1].pop()
tmpstr = ctypes.c_char_p(data + nbytes * i + 2)
res.append(tmpstr.value.decode()[0:rbyte])
except ValueError: except ValueError:
res.append(None) res.append(None)
return res return res
def _crow_nchar_to_python_block(data, num_of_rows, nbytes=None, micro=False): def _crow_nchar_to_python_block(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C nchar row to python row """Function to convert C nchar row to python row
""" """
assert(nbytes is not None) assert(nbytes is not None)
res=[] res = []
if num_of_rows >= 0: if num_of_rows >= 0:
for i in range(abs(num_of_rows)): for i in range(abs(num_of_rows)):
try: try:
tmpstr = ctypes.c_char_p(data+nbytes*i+2) tmpstr = ctypes.c_char_p(data + nbytes * i + 2)
res.append( tmpstr.value.decode() ) res.append(tmpstr.value.decode())
except ValueError: except ValueError:
res.append(None) res.append(None)
else: else:
for i in range(abs(num_of_rows)): for i in range(abs(num_of_rows)):
try: try:
res.append( (ctypes.cast(data+nbytes*i+2, ctypes.POINTER(ctypes.c_wchar * (nbytes//4))))[0].value ) res.append((ctypes.cast(data + nbytes * i + 2,
ctypes.POINTER(ctypes.c_wchar * (nbytes // 4))))[0].value)
except ValueError: except ValueError:
res.append(None) res.append(None)
return res return res
_CONVERT_FUNC = { _CONVERT_FUNC = {
FieldType.C_BOOL: _crow_bool_to_python, FieldType.C_BOOL: _crow_bool_to_python,
FieldType.C_TINYINT : _crow_tinyint_to_python, FieldType.C_TINYINT: _crow_tinyint_to_python,
FieldType.C_SMALLINT : _crow_smallint_to_python, FieldType.C_SMALLINT: _crow_smallint_to_python,
FieldType.C_INT : _crow_int_to_python, FieldType.C_INT: _crow_int_to_python,
FieldType.C_BIGINT : _crow_bigint_to_python, FieldType.C_BIGINT: _crow_bigint_to_python,
FieldType.C_FLOAT : _crow_float_to_python, FieldType.C_FLOAT: _crow_float_to_python,
FieldType.C_DOUBLE : _crow_double_to_python, FieldType.C_DOUBLE: _crow_double_to_python,
FieldType.C_BINARY: _crow_binary_to_python, FieldType.C_BINARY: _crow_binary_to_python,
FieldType.C_TIMESTAMP : _crow_timestamp_to_python, FieldType.C_TIMESTAMP: _crow_timestamp_to_python,
FieldType.C_NCHAR : _crow_nchar_to_python FieldType.C_NCHAR: _crow_nchar_to_python,
FieldType.C_TINYINT_UNSIGNED: _crow_tinyint_unsigned_to_python,
FieldType.C_SMALLINT_UNSIGNED: _crow_smallint_unsigned_to_python,
FieldType.C_INT_UNSIGNED: _crow_int_unsigned_to_python,
FieldType.C_BIGINT_UNSIGNED: _crow_bigint_unsigned_to_python
} }
_CONVERT_FUNC_BLOCK = { _CONVERT_FUNC_BLOCK = {
FieldType.C_BOOL: _crow_bool_to_python, FieldType.C_BOOL: _crow_bool_to_python,
FieldType.C_TINYINT : _crow_tinyint_to_python, FieldType.C_TINYINT: _crow_tinyint_to_python,
FieldType.C_SMALLINT : _crow_smallint_to_python, FieldType.C_SMALLINT: _crow_smallint_to_python,
FieldType.C_INT : _crow_int_to_python, FieldType.C_INT: _crow_int_to_python,
FieldType.C_BIGINT : _crow_bigint_to_python, FieldType.C_BIGINT: _crow_bigint_to_python,
FieldType.C_FLOAT : _crow_float_to_python, FieldType.C_FLOAT: _crow_float_to_python,
FieldType.C_DOUBLE : _crow_double_to_python, FieldType.C_DOUBLE: _crow_double_to_python,
FieldType.C_BINARY: _crow_binary_to_python_block, FieldType.C_BINARY: _crow_binary_to_python_block,
FieldType.C_TIMESTAMP : _crow_timestamp_to_python, FieldType.C_TIMESTAMP: _crow_timestamp_to_python,
FieldType.C_NCHAR : _crow_nchar_to_python_block FieldType.C_NCHAR: _crow_nchar_to_python_block,
FieldType.C_TINYINT_UNSIGNED: _crow_tinyint_unsigned_to_python,
FieldType.C_SMALLINT_UNSIGNED: _crow_smallint_unsigned_to_python,
FieldType.C_INT_UNSIGNED: _crow_int_unsigned_to_python,
FieldType.C_BIGINT_UNSIGNED: _crow_bigint_unsigned_to_python
} }
# Corresponding TAOS_FIELD structure in C # Corresponding TAOS_FIELD structure in C
class TaosField(ctypes.Structure): class TaosField(ctypes.Structure):
_fields_ = [('name', ctypes.c_char * 65), _fields_ = [('name', ctypes.c_char * 65),
('type', ctypes.c_char), ('type', ctypes.c_char),
('bytes', ctypes.c_short)] ('bytes', ctypes.c_short)]
# C interface class # C interface class
class CTaosInterface(object): class CTaosInterface(object):
libtaos = ctypes.CDLL('libtaos.so') libtaos = ctypes.CDLL('libtaos.so')
@ -216,7 +360,7 @@ class CTaosInterface(object):
except AttributeError: except AttributeError:
raise AttributeError("config is expected as a str") raise AttributeError("config is expected as a str")
if config != None: if config is not None:
CTaosInterface.libtaos.taos_options(3, self._config) CTaosInterface.libtaos.taos_options(3, self._config)
CTaosInterface.libtaos.taos_init() CTaosInterface.libtaos.taos_init()
@ -227,7 +371,13 @@ class CTaosInterface(object):
""" """
return self._config return self._config
def connect(self, host=None, user="root", password="taosdata", db=None, port=0): def connect(
self,
host=None,
user="root",
password="taosdata",
db=None,
port=0):
''' '''
Function to connect to server Function to connect to server
@ -236,7 +386,7 @@ class CTaosInterface(object):
# host # host
try: try:
_host = ctypes.c_char_p(host.encode( _host = ctypes.c_char_p(host.encode(
"utf-8")) if host != None else ctypes.c_char_p(None) "utf-8")) if host is not None else ctypes.c_char_p(None)
except AttributeError: except AttributeError:
raise AttributeError("host is expected as a str") raise AttributeError("host is expected as a str")
@ -255,7 +405,7 @@ class CTaosInterface(object):
# db # db
try: try:
_db = ctypes.c_char_p( _db = ctypes.c_char_p(
db.encode("utf-8")) if db != None else ctypes.c_char_p(None) db.encode("utf-8")) if db is not None else ctypes.c_char_p(None)
except AttributeError: except AttributeError:
raise AttributeError("db is expected as a str") raise AttributeError("db is expected as a str")
@ -268,11 +418,11 @@ class CTaosInterface(object):
connection = ctypes.c_void_p(CTaosInterface.libtaos.taos_connect( connection = ctypes.c_void_p(CTaosInterface.libtaos.taos_connect(
_host, _user, _password, _db, _port)) _host, _user, _password, _db, _port))
if connection.value == None: if connection.value is None:
print('connect to TDengine failed') print('connect to TDengine failed')
raise ConnectionError("connect to TDengine failed") raise ConnectionError("connect to TDengine failed")
# sys.exit(1) # sys.exit(1)
#else: # else:
# print('connect to TDengine success') # print('connect to TDengine success')
return connection return connection
@ -293,7 +443,8 @@ class CTaosInterface(object):
@rtype: 0 on success and -1 on failure @rtype: 0 on success and -1 on failure
''' '''
try: try:
return CTaosInterface.libtaos.taos_query(connection, ctypes.c_char_p(sql.encode('utf-8'))) return CTaosInterface.libtaos.taos_query(
connection, ctypes.c_char_p(sql.encode('utf-8')))
except AttributeError: except AttributeError:
raise AttributeError("sql is expected as a string") raise AttributeError("sql is expected as a string")
# finally: # finally:
@ -308,7 +459,7 @@ class CTaosInterface(object):
@staticmethod @staticmethod
def subscribe(connection, restart, topic, sql, interval): def subscribe(connection, restart, topic, sql, interval):
"""Create a subscription """Create a subscription
@restart boolean, @restart boolean,
@sql string, sql statement for data query, must be a 'select' statement. @sql string, sql statement for data query, must be a 'select' statement.
@topic string, name of this subscription @topic string, name of this subscription
""" """
@ -360,35 +511,49 @@ class CTaosInterface(object):
result, ctypes.byref(pblock)) result, ctypes.byref(pblock))
if num_of_rows == 0: if num_of_rows == 0:
return None, 0 return None, 0
isMicro = (CTaosInterface.libtaos.taos_result_precision(result) == FieldType.C_TIMESTAMP_MICRO) isMicro = (CTaosInterface.libtaos.taos_result_precision(
result) == FieldType.C_TIMESTAMP_MICRO)
blocks = [None] * len(fields) blocks = [None] * len(fields)
fieldL = CTaosInterface.libtaos.taos_fetch_lengths(result) fieldL = CTaosInterface.libtaos.taos_fetch_lengths(result)
fieldLen = [ele for ele in ctypes.cast(fieldL, ctypes.POINTER(ctypes.c_int))[:len(fields)]] fieldLen = [
ele for ele in ctypes.cast(
fieldL, ctypes.POINTER(
ctypes.c_int))[
:len(fields)]]
for i in range(len(fields)): for i in range(len(fields)):
data = ctypes.cast(pblock, ctypes.POINTER(ctypes.c_void_p))[i] data = ctypes.cast(pblock, ctypes.POINTER(ctypes.c_void_p))[i]
if fields[i]['type'] not in _CONVERT_FUNC_BLOCK: if fields[i]['type'] not in _CONVERT_FUNC_BLOCK:
raise DatabaseError("Invalid data type returned from database") raise DatabaseError("Invalid data type returned from database")
blocks[i] = _CONVERT_FUNC_BLOCK[fields[i]['type']](data, num_of_rows, fieldLen[i], isMicro) blocks[i] = _CONVERT_FUNC_BLOCK[fields[i]['type']](
data, num_of_rows, fieldLen[i], isMicro)
return blocks, abs(num_of_rows) return blocks, abs(num_of_rows)
@staticmethod @staticmethod
def fetchRow(result, fields): def fetchRow(result, fields):
pblock = ctypes.c_void_p(0) pblock = ctypes.c_void_p(0)
pblock = CTaosInterface.libtaos.taos_fetch_row(result) pblock = CTaosInterface.libtaos.taos_fetch_row(result)
if pblock : if pblock:
num_of_rows = 1 num_of_rows = 1
isMicro = (CTaosInterface.libtaos.taos_result_precision(result) == FieldType.C_TIMESTAMP_MICRO) isMicro = (CTaosInterface.libtaos.taos_result_precision(
result) == FieldType.C_TIMESTAMP_MICRO)
blocks = [None] * len(fields) blocks = [None] * len(fields)
fieldL = CTaosInterface.libtaos.taos_fetch_lengths(result) fieldL = CTaosInterface.libtaos.taos_fetch_lengths(result)
fieldLen = [ele for ele in ctypes.cast(fieldL, ctypes.POINTER(ctypes.c_int))[:len(fields)]] fieldLen = [
ele for ele in ctypes.cast(
fieldL, ctypes.POINTER(
ctypes.c_int))[
:len(fields)]]
for i in range(len(fields)): for i in range(len(fields)):
data = ctypes.cast(pblock, ctypes.POINTER(ctypes.c_void_p))[i] data = ctypes.cast(pblock, ctypes.POINTER(ctypes.c_void_p))[i]
if fields[i]['type'] not in _CONVERT_FUNC: if fields[i]['type'] not in _CONVERT_FUNC:
raise DatabaseError("Invalid data type returned from database") raise DatabaseError(
"Invalid data type returned from database")
if data is None: if data is None:
blocks[i] = [None] blocks[i] = [None]
else: else:
blocks[i] = _CONVERT_FUNC[fields[i]['type']](data, num_of_rows, fieldLen[i], isMicro) blocks[i] = _CONVERT_FUNC[fields[i]['type']](
data, num_of_rows, fieldLen[i], isMicro)
else: else:
return None, 0 return None, 0
return blocks, abs(num_of_rows) return blocks, abs(num_of_rows)

View File

@ -2,9 +2,11 @@ from .cursor import TDengineCursor
from .subscription import TDengineSubscription from .subscription import TDengineSubscription
from .cinterface import CTaosInterface from .cinterface import CTaosInterface
class TDengineConnection(object): class TDengineConnection(object):
""" TDengine connection object """ TDengine connection object
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self._conn = None self._conn = None
self._host = None self._host = None
@ -29,7 +31,7 @@ class TDengineConnection(object):
# password # password
if 'password' in kwargs: if 'password' in kwargs:
self._password = kwargs['password'] self._password = kwargs['password']
# database # database
if 'database' in kwargs: if 'database' in kwargs:
self._database = kwargs['database'] self._database = kwargs['database']
@ -43,7 +45,12 @@ class TDengineConnection(object):
self._config = kwargs['config'] self._config = kwargs['config']
self._chandle = CTaosInterface(self._config) self._chandle = CTaosInterface(self._config)
self._conn = self._chandle.connect(self._host, self._user, self._password, self._database, self._port) self._conn = self._chandle.connect(
self._host,
self._user,
self._password,
self._database,
self._port)
def close(self): def close(self):
"""Close current connection. """Close current connection.
@ -55,7 +62,8 @@ class TDengineConnection(object):
""" """
if self._conn is None: if self._conn is None:
return None return None
sub = CTaosInterface.subscribe(self._conn, restart, topic, sql, interval) sub = CTaosInterface.subscribe(
self._conn, restart, topic, sql, interval)
return TDengineSubscription(sub) return TDengineSubscription(sub)
def cursor(self): def cursor(self):
@ -80,7 +88,8 @@ class TDengineConnection(object):
""" """
pass pass
if __name__ == "__main__": if __name__ == "__main__":
conn = TDengineConnection(host='192.168.1.107') conn = TDengineConnection(host='192.168.1.107')
conn.close() conn.close()
print("Hello world") print("Hello world")

View File

@ -3,6 +3,7 @@
from .dbapi import * from .dbapi import *
class FieldType(object): class FieldType(object):
"""TDengine Field Types """TDengine Field Types
""" """
@ -18,13 +19,21 @@ class FieldType(object):
C_BINARY = 8 C_BINARY = 8
C_TIMESTAMP = 9 C_TIMESTAMP = 9
C_NCHAR = 10 C_NCHAR = 10
C_TINYINT_UNSIGNED = 12
C_SMALLINT_UNSIGNED = 13
C_INT_UNSIGNED = 14
C_BIGINT_UNSIGNED = 15
# NULL value definition # NULL value definition
# NOTE: These values should change according to C definition in tsdb.h # NOTE: These values should change according to C definition in tsdb.h
C_BOOL_NULL = 0x02 C_BOOL_NULL = 0x02
C_TINYINT_NULL = -128 C_TINYINT_NULL = -128
C_TINYINT_UNSIGNED_NULL = 255
C_SMALLINT_NULL = -32768 C_SMALLINT_NULL = -32768
C_SMALLINT_UNSIGNED_NULL = 65535
C_INT_NULL = -2147483648 C_INT_NULL = -2147483648
C_INT_UNSIGNED_NULL = 4294967295
C_BIGINT_NULL = -9223372036854775808 C_BIGINT_NULL = -9223372036854775808
C_BIGINT_UNSIGNED_NULL = 18446744073709551615
C_FLOAT_NULL = float('nan') C_FLOAT_NULL = float('nan')
C_DOUBLE_NULL = float('nan') C_DOUBLE_NULL = float('nan')
C_BINARY_NULL = bytearray([int('0xff', 16)]) C_BINARY_NULL = bytearray([int('0xff', 16)])

View File

@ -5,6 +5,7 @@ import threading
# querySeqNum = 0 # querySeqNum = 0
class TDengineCursor(object): class TDengineCursor(object):
"""Database cursor which is used to manage the context of a fetch operation. """Database cursor which is used to manage the context of a fetch operation.
@ -107,8 +108,8 @@ class TDengineCursor(object):
# if threading.get_ident() != self._threadId: # if threading.get_ident() != self._threadId:
# info ="Cursor execute:Thread ID not match,creater:"+str(self._threadId)+" caller:"+str(threading.get_ident()) # info ="Cursor execute:Thread ID not match,creater:"+str(self._threadId)+" caller:"+str(threading.get_ident())
# raise OperationalError(info) # raise OperationalError(info)
# print(info) # print(info)
# return None # return None
if not operation: if not operation:
return None return None
@ -137,8 +138,8 @@ class TDengineCursor(object):
if errno == 0: if errno == 0:
if CTaosInterface.fieldsCount(self._result) == 0: if CTaosInterface.fieldsCount(self._result) == 0:
self._affected_rows += CTaosInterface.affectedRows( self._affected_rows += CTaosInterface.affectedRows(
self._result ) self._result)
return CTaosInterface.affectedRows(self._result ) return CTaosInterface.affectedRows(self._result)
else: else:
self._fields = CTaosInterface.useResult( self._fields = CTaosInterface.useResult(
self._result) self._result)
@ -168,11 +169,26 @@ class TDengineCursor(object):
if (dataType.upper() == "TINYINT"): if (dataType.upper() == "TINYINT"):
if (self._description[col][1] == FieldType.C_TINYINT): if (self._description[col][1] == FieldType.C_TINYINT):
return True return True
if (dataType.upper() == "TINYINT UNSIGNED"):
if (self._description[col][1] == FieldType.C_TINYINT_UNSIGNED):
return True
if (dataType.upper() == "SMALLINT"):
if (self._description[col][1] == FieldType.C_SMALLINT):
return True
if (dataType.upper() == "SMALLINT UNSIGNED"):
if (self._description[col][1] == FieldType.C_SMALLINT_UNSIGNED):
return True
if (dataType.upper() == "INT"): if (dataType.upper() == "INT"):
if (self._description[col][1] == FieldType.C_INT): if (self._description[col][1] == FieldType.C_INT):
return True return True
if (dataType.upper() == "INT UNSIGNED"):
if (self._description[col][1] == FieldType.C_INT_UNSIGNED):
return True
if (dataType.upper() == "BIGINT"): if (dataType.upper() == "BIGINT"):
if (self._description[col][1] == FieldType.C_INT): if (self._description[col][1] == FieldType.C_BIGINT):
return True
if (dataType.upper() == "BIGINT UNSIGNED"):
if (self._description[col][1] == FieldType.C_BIGINT_UNSIGNED):
return True return True
if (dataType.upper() == "FLOAT"): if (dataType.upper() == "FLOAT"):
if (self._description[col][1] == FieldType.C_FLOAT): if (self._description[col][1] == FieldType.C_FLOAT):
@ -201,10 +217,13 @@ class TDengineCursor(object):
buffer = [[] for i in range(len(self._fields))] buffer = [[] for i in range(len(self._fields))]
self._rowcount = 0 self._rowcount = 0
while True: while True:
block, num_of_fields = CTaosInterface.fetchRow(self._result, self._fields) block, num_of_fields = CTaosInterface.fetchRow(
self._result, self._fields)
errno = CTaosInterface.libtaos.taos_errno(self._result) errno = CTaosInterface.libtaos.taos_errno(self._result)
if errno != 0: if errno != 0:
raise ProgrammingError(CTaosInterface.errStr(self._result), errno) raise ProgrammingError(
CTaosInterface.errStr(
self._result), errno)
if num_of_fields == 0: if num_of_fields == 0:
break break
self._rowcount += num_of_fields self._rowcount += num_of_fields
@ -219,15 +238,20 @@ class TDengineCursor(object):
buffer = [[] for i in range(len(self._fields))] buffer = [[] for i in range(len(self._fields))]
self._rowcount = 0 self._rowcount = 0
while True: while True:
block, num_of_fields = CTaosInterface.fetchBlock(self._result, self._fields) block, num_of_fields = CTaosInterface.fetchBlock(
self._result, self._fields)
errno = CTaosInterface.libtaos.taos_errno(self._result) errno = CTaosInterface.libtaos.taos_errno(self._result)
if errno != 0: if errno != 0:
raise ProgrammingError(CTaosInterface.errStr(self._result), errno) raise ProgrammingError(
if num_of_fields == 0: break CTaosInterface.errStr(
self._result), errno)
if num_of_fields == 0:
break
self._rowcount += num_of_fields self._rowcount += num_of_fields
for i in range(len(self._fields)): for i in range(len(self._fields)):
buffer[i].extend(block[i]) buffer[i].extend(block[i])
return list(map(tuple, zip(*buffer))) return list(map(tuple, zip(*buffer)))
def nextset(self): def nextset(self):
""" """
""" """
@ -259,8 +283,8 @@ class TDengineCursor(object):
# if threading.get_ident() != self._threadId: # if threading.get_ident() != self._threadId:
# info = "Cursor handleresult:Thread ID not match,creater:"+str(self._threadId)+" caller:"+str(threading.get_ident()) # info = "Cursor handleresult:Thread ID not match,creater:"+str(self._threadId)+" caller:"+str(threading.get_ident())
# raise OperationalError(info) # raise OperationalError(info)
# print(info) # print(info)
# return None # return None
self._description = [] self._description = []
for ele in self._fields: for ele in self._fields:
@ -268,4 +292,3 @@ class TDengineCursor(object):
(ele['name'], ele['type'], None, None, None, None, False)) (ele['name'], ele['type'], None, None, None, None, False))
return self._result return self._result

View File

@ -4,6 +4,7 @@
import time import time
import datetime import datetime
class DBAPITypeObject(object): class DBAPITypeObject(object):
def __init__(self, *values): def __init__(self, *values):
self.values = values self.values = values
@ -16,23 +17,28 @@ class DBAPITypeObject(object):
else: else:
return -1 return -1
Date = datetime.date Date = datetime.date
Time = datetime.time Time = datetime.time
Timestamp = datetime.datetime Timestamp = datetime.datetime
def DataFromTicks(ticks): def DataFromTicks(ticks):
return Date(*time.localtime(ticks)[:3]) return Date(*time.localtime(ticks)[:3])
def TimeFromTicks(ticks): def TimeFromTicks(ticks):
return Time(*time.localtime(ticks)[3:6]) return Time(*time.localtime(ticks)[3:6])
def TimestampFromTicks(ticks): def TimestampFromTicks(ticks):
return Timestamp(*time.localtime(ticks)[:6]) return Timestamp(*time.localtime(ticks)[:6])
Binary = bytes Binary = bytes
# STRING = DBAPITypeObject(*constants.FieldType.get_string_types()) # STRING = DBAPITypeObject(*constants.FieldType.get_string_types())
# BINARY = DBAPITypeObject(*constants.FieldType.get_binary_types()) # BINARY = DBAPITypeObject(*constants.FieldType.get_binary_types())
# NUMBER = BAPITypeObject(*constants.FieldType.get_number_types()) # NUMBER = BAPITypeObject(*constants.FieldType.get_number_types())
# DATETIME = DBAPITypeObject(*constants.FieldType.get_timestamp_types()) # DATETIME = DBAPITypeObject(*constants.FieldType.get_timestamp_types())
# ROWID = DBAPITypeObject() # ROWID = DBAPITypeObject()

View File

@ -1,35 +1,41 @@
"""Python exceptions """Python exceptions
""" """
class Error(Exception): class Error(Exception):
def __init__(self, msg=None, errno=None): def __init__(self, msg=None, errno=None):
self.msg = msg self.msg = msg
self._full_msg = self.msg self._full_msg = self.msg
self.errno = errno self.errno = errno
def __str__(self): def __str__(self):
return self._full_msg return self._full_msg
class Warning(Exception): class Warning(Exception):
"""Exception raised for important warnings like data truncations while inserting. """Exception raised for important warnings like data truncations while inserting.
""" """
pass pass
class InterfaceError(Error): class InterfaceError(Error):
"""Exception raised for errors that are related to the database interface rather than the database itself. """Exception raised for errors that are related to the database interface rather than the database itself.
""" """
pass pass
class DatabaseError(Error): class DatabaseError(Error):
"""Exception raised for errors that are related to the database. """Exception raised for errors that are related to the database.
""" """
pass pass
class DataError(DatabaseError): class DataError(DatabaseError):
"""Exception raised for errors that are due to problems with the processed data like division by zero, numeric value out of range. """Exception raised for errors that are due to problems with the processed data like division by zero, numeric value out of range.
""" """
pass pass
class OperationalError(DatabaseError): class OperationalError(DatabaseError):
"""Exception raised for errors that are related to the database's operation and not necessarily under the control of the programmer """Exception raised for errors that are related to the database's operation and not necessarily under the control of the programmer
""" """
@ -41,17 +47,20 @@ class IntegrityError(DatabaseError):
""" """
pass pass
class InternalError(DatabaseError): class InternalError(DatabaseError):
"""Exception raised when the database encounters an internal error. """Exception raised when the database encounters an internal error.
""" """
pass pass
class ProgrammingError(DatabaseError): class ProgrammingError(DatabaseError):
"""Exception raised for programming errors. """Exception raised for programming errors.
""" """
pass pass
class NotSupportedError(DatabaseError): class NotSupportedError(DatabaseError):
"""Exception raised in case a method or database API was used which is not supported by the database,. """Exception raised in case a method or database API was used which is not supported by the database,.
""" """
pass pass

View File

@ -1,32 +1,33 @@
from .cinterface import CTaosInterface from .cinterface import CTaosInterface
from .error import * from .error import *
class TDengineSubscription(object): class TDengineSubscription(object):
"""TDengine subscription object """TDengine subscription object
""" """
def __init__(self, sub): def __init__(self, sub):
self._sub = sub self._sub = sub
def consume(self): def consume(self):
"""Consume rows of a subscription """Consume rows of a subscription
""" """
if self._sub is None: if self._sub is None:
raise OperationalError("Invalid use of consume") raise OperationalError("Invalid use of consume")
result, fields = CTaosInterface.consume(self._sub) result, fields = CTaosInterface.consume(self._sub)
buffer = [[] for i in range(len(fields))] buffer = [[] for i in range(len(fields))]
while True: while True:
block, num_of_fields = CTaosInterface.fetchBlock(result, fields) block, num_of_fields = CTaosInterface.fetchBlock(result, fields)
if num_of_fields == 0: break if num_of_fields == 0:
break
for i in range(len(fields)): for i in range(len(fields)):
buffer[i].extend(block[i]) buffer[i].extend(block[i])
self.fields = fields self.fields = fields
return list(map(tuple, zip(*buffer))) return list(map(tuple, zip(*buffer)))
def close(self, keepProgress=True):
def close(self, keepProgress = True):
"""Close the Subscription. """Close the Subscription.
""" """
if self._sub is None: if self._sub is None:
@ -38,15 +39,19 @@ class TDengineSubscription(object):
if __name__ == '__main__': if __name__ == '__main__':
from .connection import TDengineConnection from .connection import TDengineConnection
conn = TDengineConnection(host="127.0.0.1", user="root", password="taosdata", database="test") conn = TDengineConnection(
host="127.0.0.1",
user="root",
password="taosdata",
database="test")
# Generate a cursor object to run SQL commands # Generate a cursor object to run SQL commands
sub = conn.subscribe(True, "test", "select * from meters;", 1000) sub = conn.subscribe(True, "test", "select * from meters;", 1000)
for i in range(0,10): for i in range(0, 10):
data = sub.consume() data = sub.consume()
for d in data: for d in data:
print(d) print(d)
sub.close() sub.close()
conn.close() conn.close()

View File

@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
setuptools.setup( setuptools.setup(
name="taos", name="taos",
version="2.0.4", version="2.0.5",
author="Taosdata Inc.", author="Taosdata Inc.",
author_email="support@taosdata.com", author_email="support@taosdata.com",
description="TDengine python client package", description="TDengine python client package",

View File

@ -3,12 +3,12 @@ from .connection import TDengineConnection
from .cursor import TDengineCursor from .cursor import TDengineCursor
# Globals # Globals
apilevel = '2.0.4'
threadsafety = 0 threadsafety = 0
paramstyle = 'pyformat' paramstyle = 'pyformat'
__all__ = ['connection', 'cursor'] __all__ = ['connection', 'cursor']
def connect(*args, **kwargs): def connect(*args, **kwargs):
""" Function to return a TDengine connector object """ Function to return a TDengine connector object

View File

@ -4,11 +4,14 @@ from .error import *
import math import math
import datetime import datetime
def _convert_millisecond_to_datetime(milli): def _convert_millisecond_to_datetime(milli):
return datetime.datetime.fromtimestamp(milli/1000.0) return datetime.datetime.fromtimestamp(milli / 1000.0)
def _convert_microsecond_to_datetime(micro): def _convert_microsecond_to_datetime(micro):
return datetime.datetime.fromtimestamp(micro/1000000.0) return datetime.datetime.fromtimestamp(micro / 1000000.0)
def _crow_timestamp_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_timestamp_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C bool row to python row """Function to convert C bool row to python row
@ -18,168 +21,309 @@ def _crow_timestamp_to_python(data, num_of_rows, nbytes=None, micro=False):
_timestamp_converter = _convert_microsecond_to_datetime _timestamp_converter = _convert_microsecond_to_datetime
if num_of_rows > 0: if num_of_rows > 0:
return list(map(_timestamp_converter, ctypes.cast(data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)])) return list(map(_timestamp_converter, ctypes.cast(
data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)]))
else: else:
return list(map(_timestamp_converter, ctypes.cast(data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)])) return list(map(_timestamp_converter, ctypes.cast(
data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)]))
def _crow_bool_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_bool_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C bool row to python row """Function to convert C bool row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele == FieldType.C_BOOL_NULL else bool(ele) for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_byte))[:abs(num_of_rows)] ] return [
None if ele == FieldType.C_BOOL_NULL else bool(ele) for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_byte))[
:abs(num_of_rows)]]
else: else:
return [ None if ele == FieldType.C_BOOL_NULL else bool(ele) for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_bool))[:abs(num_of_rows)] ] return [
None if ele == FieldType.C_BOOL_NULL else bool(ele) for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_bool))[
:abs(num_of_rows)]]
def _crow_tinyint_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_tinyint_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C tinyint row to python row """Function to convert C tinyint row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele == FieldType.C_TINYINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_byte))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_TINYINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_byte))[:abs(num_of_rows)]]
else: else:
return [ None if ele == FieldType.C_TINYINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_byte))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_TINYINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_byte))[:abs(num_of_rows)]]
def _crow_tinyint_unsigned_to_python(
data,
num_of_rows,
nbytes=None,
micro=False):
"""Function to convert C tinyint row to python row
"""
if num_of_rows > 0:
return [
None if ele == FieldType.C_TINYINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_byte))[
:abs(num_of_rows)]]
else:
return [
None if ele == FieldType.C_TINYINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_byte))[
:abs(num_of_rows)]]
def _crow_smallint_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_smallint_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C smallint row to python row """Function to convert C smallint row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele == FieldType.C_SMALLINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_short))[:abs(num_of_rows)]] return [
None if ele == FieldType.C_SMALLINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_short))[
:abs(num_of_rows)]]
else: else:
return [ None if ele == FieldType.C_SMALLINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_short))[:abs(num_of_rows)] ] return [
None if ele == FieldType.C_SMALLINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_short))[
:abs(num_of_rows)]]
def _crow_smallint_unsigned_to_python(
data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C smallint row to python row
"""
if num_of_rows > 0:
return [
None if ele == FieldType.C_SMALLINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_short))[
:abs(num_of_rows)]]
else:
return [
None if ele == FieldType.C_SMALLINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_short))[
:abs(num_of_rows)]]
def _crow_int_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_int_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C int row to python row """Function to convert C int row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele == FieldType.C_INT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_int))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_INT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_int))[:abs(num_of_rows)]]
else: else:
return [ None if ele == FieldType.C_INT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_int))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_INT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_int))[:abs(num_of_rows)]]
def _crow_int_unsigned_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C int row to python row
"""
if num_of_rows > 0:
return [
None if ele == FieldType.C_INT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_int))[
:abs(num_of_rows)]]
else:
return [
None if ele == FieldType.C_INT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_int))[
:abs(num_of_rows)]]
def _crow_bigint_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_bigint_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C bigint row to python row """Function to convert C bigint row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele == FieldType.C_BIGINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_BIGINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)]]
else: else:
return [ None if ele == FieldType.C_BIGINT_NULL else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)] ] return [None if ele == FieldType.C_BIGINT_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_long))[:abs(num_of_rows)]]
def _crow_bigint_unsigned_to_python(
data,
num_of_rows,
nbytes=None,
micro=False):
"""Function to convert C bigint row to python row
"""
if num_of_rows > 0:
return [
None if ele == FieldType.C_BIGINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_long))[
:abs(num_of_rows)]]
else:
return [
None if ele == FieldType.C_BIGINT_UNSIGNED_NULL else ele for ele in ctypes.cast(
data, ctypes.POINTER(
ctypes.c_long))[
:abs(num_of_rows)]]
def _crow_float_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_float_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C float row to python row """Function to convert C float row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if math.isnan(ele) else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_float))[:abs(num_of_rows)] ] return [None if math.isnan(ele) else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_float))[:abs(num_of_rows)]]
else: else:
return [ None if math.isnan(ele) else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_float))[:abs(num_of_rows)] ] return [None if math.isnan(ele) else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_float))[:abs(num_of_rows)]]
def _crow_double_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_double_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C double row to python row """Function to convert C double row to python row
""" """
if num_of_rows > 0: if num_of_rows > 0:
return [ None if math.isnan(ele) else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_double))[:abs(num_of_rows)] ] return [None if math.isnan(ele) else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_double))[:abs(num_of_rows)]]
else: else:
return [ None if math.isnan(ele) else ele for ele in ctypes.cast(data, ctypes.POINTER(ctypes.c_double))[:abs(num_of_rows)] ] return [None if math.isnan(ele) else ele for ele in ctypes.cast(
data, ctypes.POINTER(ctypes.c_double))[:abs(num_of_rows)]]
def _crow_binary_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_binary_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C binary row to python row """Function to convert C binary row to python row
""" """
assert(nbytes is not None) assert(nbytes is not None)
if num_of_rows > 0: if num_of_rows > 0:
return [ None if ele.value[0:1] == FieldType.C_BINARY_NULL else ele.value.decode('utf-8') for ele in (ctypes.cast(data, ctypes.POINTER(ctypes.c_char * nbytes)))[:abs(num_of_rows)]] return [None if ele.value[0:1] == FieldType.C_BINARY_NULL else ele.value.decode(
'utf-8') for ele in (ctypes.cast(data, ctypes.POINTER(ctypes.c_char * nbytes)))[:abs(num_of_rows)]]
else: else:
return [ None if ele.value[0:1] == FieldType.C_BINARY_NULL else ele.value.decode('utf-8') for ele in (ctypes.cast(data, ctypes.POINTER(ctypes.c_char * nbytes)))[:abs(num_of_rows)]] return [None if ele.value[0:1] == FieldType.C_BINARY_NULL else ele.value.decode(
'utf-8') for ele in (ctypes.cast(data, ctypes.POINTER(ctypes.c_char * nbytes)))[:abs(num_of_rows)]]
def _crow_nchar_to_python(data, num_of_rows, nbytes=None, micro=False): def _crow_nchar_to_python(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C nchar row to python row """Function to convert C nchar row to python row
""" """
assert(nbytes is not None) assert(nbytes is not None)
res=[] res = []
for i in range(abs(num_of_rows)): for i in range(abs(num_of_rows)):
try: try:
if num_of_rows >= 0: if num_of_rows >= 0:
tmpstr = ctypes.c_char_p(data) tmpstr = ctypes.c_char_p(data)
res.append( tmpstr.value.decode() ) res.append(tmpstr.value.decode())
else: else:
res.append( (ctypes.cast(data+nbytes*i, ctypes.POINTER(ctypes.c_wchar * (nbytes//4))))[0].value ) res.append((ctypes.cast(data + nbytes * i,
ctypes.POINTER(ctypes.c_wchar * (nbytes // 4))))[0].value)
except ValueError: except ValueError:
res.append(None) res.append(None)
return res return res
def _crow_binary_to_python_block(data, num_of_rows, nbytes=None, micro=False): def _crow_binary_to_python_block(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C binary row to python row """Function to convert C binary row to python row
""" """
assert(nbytes is not None) assert(nbytes is not None)
res=[] res = []
if num_of_rows > 0: if num_of_rows > 0:
for i in range(abs(num_of_rows)): for i in range(abs(num_of_rows)):
try: try:
rbyte=ctypes.cast(data+nbytes*i,ctypes.POINTER(ctypes.c_short))[:1].pop() rbyte = ctypes.cast(
tmpstr = ctypes.c_char_p(data+nbytes*i+2) data + nbytes * i,
res.append( tmpstr.value.decode()[0:rbyte] ) ctypes.POINTER(
ctypes.c_short))[
:1].pop()
tmpstr = ctypes.c_char_p(data + nbytes * i + 2)
res.append(tmpstr.value.decode()[0:rbyte])
except ValueError: except ValueError:
res.append(None) res.append(None)
else: else:
for i in range(abs(num_of_rows)): for i in range(abs(num_of_rows)):
try: try:
rbyte=ctypes.cast(data+nbytes*i,ctypes.POINTER(ctypes.c_short))[:1].pop() rbyte = ctypes.cast(
tmpstr = ctypes.c_char_p(data+nbytes*i+2) data + nbytes * i,
res.append( tmpstr.value.decode()[0:rbyte] ) ctypes.POINTER(
ctypes.c_short))[
:1].pop()
tmpstr = ctypes.c_char_p(data + nbytes * i + 2)
res.append(tmpstr.value.decode()[0:rbyte])
except ValueError: except ValueError:
res.append(None) res.append(None)
return res return res
def _crow_nchar_to_python_block(data, num_of_rows, nbytes=None, micro=False): def _crow_nchar_to_python_block(data, num_of_rows, nbytes=None, micro=False):
"""Function to convert C nchar row to python row """Function to convert C nchar row to python row
""" """
assert(nbytes is not None) assert(nbytes is not None)
res=[] res = []
if num_of_rows >= 0: if num_of_rows >= 0:
for i in range(abs(num_of_rows)): for i in range(abs(num_of_rows)):
try: try:
tmpstr = ctypes.c_char_p(data+nbytes*i+2) tmpstr = ctypes.c_char_p(data + nbytes * i + 2)
res.append( tmpstr.value.decode() ) res.append(tmpstr.value.decode())
except ValueError: except ValueError:
res.append(None) res.append(None)
else: else:
for i in range(abs(num_of_rows)): for i in range(abs(num_of_rows)):
try: try:
res.append( (ctypes.cast(data+nbytes*i+2, ctypes.POINTER(ctypes.c_wchar * (nbytes//4))))[0].value ) res.append((ctypes.cast(data + nbytes * i + 2,
ctypes.POINTER(ctypes.c_wchar * (nbytes // 4))))[0].value)
except ValueError: except ValueError:
res.append(None) res.append(None)
return res return res
_CONVERT_FUNC = { _CONVERT_FUNC = {
FieldType.C_BOOL: _crow_bool_to_python, FieldType.C_BOOL: _crow_bool_to_python,
FieldType.C_TINYINT : _crow_tinyint_to_python, FieldType.C_TINYINT: _crow_tinyint_to_python,
FieldType.C_SMALLINT : _crow_smallint_to_python, FieldType.C_SMALLINT: _crow_smallint_to_python,
FieldType.C_INT : _crow_int_to_python, FieldType.C_INT: _crow_int_to_python,
FieldType.C_BIGINT : _crow_bigint_to_python, FieldType.C_BIGINT: _crow_bigint_to_python,
FieldType.C_FLOAT : _crow_float_to_python, FieldType.C_FLOAT: _crow_float_to_python,
FieldType.C_DOUBLE : _crow_double_to_python, FieldType.C_DOUBLE: _crow_double_to_python,
FieldType.C_BINARY: _crow_binary_to_python, FieldType.C_BINARY: _crow_binary_to_python,
FieldType.C_TIMESTAMP : _crow_timestamp_to_python, FieldType.C_TIMESTAMP: _crow_timestamp_to_python,
FieldType.C_NCHAR : _crow_nchar_to_python FieldType.C_NCHAR: _crow_nchar_to_python,
FieldType.C_TINYINT_UNSIGNED: _crow_tinyint_unsigned_to_python,
FieldType.C_SMALLINT_UNSIGNED: _crow_smallint_unsigned_to_python,
FieldType.C_INT_UNSIGNED: _crow_int_unsigned_to_python,
FieldType.C_BIGINT_UNSIGNED: _crow_bigint_unsigned_to_python
} }
_CONVERT_FUNC_BLOCK = { _CONVERT_FUNC_BLOCK = {
FieldType.C_BOOL: _crow_bool_to_python, FieldType.C_BOOL: _crow_bool_to_python,
FieldType.C_TINYINT : _crow_tinyint_to_python, FieldType.C_TINYINT: _crow_tinyint_to_python,
FieldType.C_SMALLINT : _crow_smallint_to_python, FieldType.C_SMALLINT: _crow_smallint_to_python,
FieldType.C_INT : _crow_int_to_python, FieldType.C_INT: _crow_int_to_python,
FieldType.C_BIGINT : _crow_bigint_to_python, FieldType.C_BIGINT: _crow_bigint_to_python,
FieldType.C_FLOAT : _crow_float_to_python, FieldType.C_FLOAT: _crow_float_to_python,
FieldType.C_DOUBLE : _crow_double_to_python, FieldType.C_DOUBLE: _crow_double_to_python,
FieldType.C_BINARY: _crow_binary_to_python_block, FieldType.C_BINARY: _crow_binary_to_python_block,
FieldType.C_TIMESTAMP : _crow_timestamp_to_python, FieldType.C_TIMESTAMP: _crow_timestamp_to_python,
FieldType.C_NCHAR : _crow_nchar_to_python_block FieldType.C_NCHAR: _crow_nchar_to_python_block,
FieldType.C_TINYINT_UNSIGNED: _crow_tinyint_unsigned_to_python,
FieldType.C_SMALLINT_UNSIGNED: _crow_smallint_unsigned_to_python,
FieldType.C_INT_UNSIGNED: _crow_int_unsigned_to_python,
FieldType.C_BIGINT_UNSIGNED: _crow_bigint_unsigned_to_python
} }
# Corresponding TAOS_FIELD structure in C # Corresponding TAOS_FIELD structure in C
class TaosField(ctypes.Structure): class TaosField(ctypes.Structure):
_fields_ = [('name', ctypes.c_char * 65), _fields_ = [('name', ctypes.c_char * 65),
('type', ctypes.c_char), ('type', ctypes.c_char),
('bytes', ctypes.c_short)] ('bytes', ctypes.c_short)]
# C interface class # C interface class
class CTaosInterface(object): class CTaosInterface(object):
libtaos = ctypes.CDLL('libtaos.dylib') libtaos = ctypes.CDLL('libtaos.dylib')
@ -216,7 +360,7 @@ class CTaosInterface(object):
except AttributeError: except AttributeError:
raise AttributeError("config is expected as a str") raise AttributeError("config is expected as a str")
if config != None: if config is not None:
CTaosInterface.libtaos.taos_options(3, self._config) CTaosInterface.libtaos.taos_options(3, self._config)
CTaosInterface.libtaos.taos_init() CTaosInterface.libtaos.taos_init()
@ -227,7 +371,13 @@ class CTaosInterface(object):
""" """
return self._config return self._config
def connect(self, host=None, user="root", password="taosdata", db=None, port=0): def connect(
self,
host=None,
user="root",
password="taosdata",
db=None,
port=0):
''' '''
Function to connect to server Function to connect to server
@ -236,7 +386,7 @@ class CTaosInterface(object):
# host # host
try: try:
_host = ctypes.c_char_p(host.encode( _host = ctypes.c_char_p(host.encode(
"utf-8")) if host != None else ctypes.c_char_p(None) "utf-8")) if host is not None else ctypes.c_char_p(None)
except AttributeError: except AttributeError:
raise AttributeError("host is expected as a str") raise AttributeError("host is expected as a str")
@ -255,7 +405,7 @@ class CTaosInterface(object):
# db # db
try: try:
_db = ctypes.c_char_p( _db = ctypes.c_char_p(
db.encode("utf-8")) if db != None else ctypes.c_char_p(None) db.encode("utf-8")) if db is not None else ctypes.c_char_p(None)
except AttributeError: except AttributeError:
raise AttributeError("db is expected as a str") raise AttributeError("db is expected as a str")
@ -268,11 +418,11 @@ class CTaosInterface(object):
connection = ctypes.c_void_p(CTaosInterface.libtaos.taos_connect( connection = ctypes.c_void_p(CTaosInterface.libtaos.taos_connect(
_host, _user, _password, _db, _port)) _host, _user, _password, _db, _port))
if connection.value == None: if connection.value is None:
print('connect to TDengine failed') print('connect to TDengine failed')
raise ConnectionError("connect to TDengine failed") raise ConnectionError("connect to TDengine failed")
# sys.exit(1) # sys.exit(1)
#else: # else:
# print('connect to TDengine success') # print('connect to TDengine success')
return connection return connection
@ -293,7 +443,8 @@ class CTaosInterface(object):
@rtype: 0 on success and -1 on failure @rtype: 0 on success and -1 on failure
''' '''
try: try:
return CTaosInterface.libtaos.taos_query(connection, ctypes.c_char_p(sql.encode('utf-8'))) return CTaosInterface.libtaos.taos_query(
connection, ctypes.c_char_p(sql.encode('utf-8')))
except AttributeError: except AttributeError:
raise AttributeError("sql is expected as a string") raise AttributeError("sql is expected as a string")
# finally: # finally:
@ -308,7 +459,7 @@ class CTaosInterface(object):
@staticmethod @staticmethod
def subscribe(connection, restart, topic, sql, interval): def subscribe(connection, restart, topic, sql, interval):
"""Create a subscription """Create a subscription
@restart boolean, @restart boolean,
@sql string, sql statement for data query, must be a 'select' statement. @sql string, sql statement for data query, must be a 'select' statement.
@topic string, name of this subscription @topic string, name of this subscription
""" """
@ -360,35 +511,49 @@ class CTaosInterface(object):
result, ctypes.byref(pblock)) result, ctypes.byref(pblock))
if num_of_rows == 0: if num_of_rows == 0:
return None, 0 return None, 0
isMicro = (CTaosInterface.libtaos.taos_result_precision(result) == FieldType.C_TIMESTAMP_MICRO) isMicro = (CTaosInterface.libtaos.taos_result_precision(
result) == FieldType.C_TIMESTAMP_MICRO)
blocks = [None] * len(fields) blocks = [None] * len(fields)
fieldL = CTaosInterface.libtaos.taos_fetch_lengths(result) fieldL = CTaosInterface.libtaos.taos_fetch_lengths(result)
fieldLen = [ele for ele in ctypes.cast(fieldL, ctypes.POINTER(ctypes.c_int))[:len(fields)]] fieldLen = [
ele for ele in ctypes.cast(
fieldL, ctypes.POINTER(
ctypes.c_int))[
:len(fields)]]
for i in range(len(fields)): for i in range(len(fields)):
data = ctypes.cast(pblock, ctypes.POINTER(ctypes.c_void_p))[i] data = ctypes.cast(pblock, ctypes.POINTER(ctypes.c_void_p))[i]
if fields[i]['type'] not in _CONVERT_FUNC_BLOCK: if fields[i]['type'] not in _CONVERT_FUNC_BLOCK:
raise DatabaseError("Invalid data type returned from database") raise DatabaseError("Invalid data type returned from database")
blocks[i] = _CONVERT_FUNC_BLOCK[fields[i]['type']](data, num_of_rows, fieldLen[i], isMicro) blocks[i] = _CONVERT_FUNC_BLOCK[fields[i]['type']](
data, num_of_rows, fieldLen[i], isMicro)
return blocks, abs(num_of_rows) return blocks, abs(num_of_rows)
@staticmethod @staticmethod
def fetchRow(result, fields): def fetchRow(result, fields):
pblock = ctypes.c_void_p(0) pblock = ctypes.c_void_p(0)
pblock = CTaosInterface.libtaos.taos_fetch_row(result) pblock = CTaosInterface.libtaos.taos_fetch_row(result)
if pblock : if pblock:
num_of_rows = 1 num_of_rows = 1
isMicro = (CTaosInterface.libtaos.taos_result_precision(result) == FieldType.C_TIMESTAMP_MICRO) isMicro = (CTaosInterface.libtaos.taos_result_precision(
result) == FieldType.C_TIMESTAMP_MICRO)
blocks = [None] * len(fields) blocks = [None] * len(fields)
fieldL = CTaosInterface.libtaos.taos_fetch_lengths(result) fieldL = CTaosInterface.libtaos.taos_fetch_lengths(result)
fieldLen = [ele for ele in ctypes.cast(fieldL, ctypes.POINTER(ctypes.c_int))[:len(fields)]] fieldLen = [
ele for ele in ctypes.cast(
fieldL, ctypes.POINTER(
ctypes.c_int))[
:len(fields)]]
for i in range(len(fields)): for i in range(len(fields)):
data = ctypes.cast(pblock, ctypes.POINTER(ctypes.c_void_p))[i] data = ctypes.cast(pblock, ctypes.POINTER(ctypes.c_void_p))[i]
if fields[i]['type'] not in _CONVERT_FUNC: if fields[i]['type'] not in _CONVERT_FUNC:
raise DatabaseError("Invalid data type returned from database") raise DatabaseError(
"Invalid data type returned from database")
if data is None: if data is None:
blocks[i] = [None] blocks[i] = [None]
else: else:
blocks[i] = _CONVERT_FUNC[fields[i]['type']](data, num_of_rows, fieldLen[i], isMicro) blocks[i] = _CONVERT_FUNC[fields[i]['type']](
data, num_of_rows, fieldLen[i], isMicro)
else: else:
return None, 0 return None, 0
return blocks, abs(num_of_rows) return blocks, abs(num_of_rows)

View File

@ -2,9 +2,11 @@ from .cursor import TDengineCursor
from .subscription import TDengineSubscription from .subscription import TDengineSubscription
from .cinterface import CTaosInterface from .cinterface import CTaosInterface
class TDengineConnection(object): class TDengineConnection(object):
""" TDengine connection object """ TDengine connection object
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self._conn = None self._conn = None
self._host = None self._host = None
@ -29,7 +31,7 @@ class TDengineConnection(object):
# password # password
if 'password' in kwargs: if 'password' in kwargs:
self._password = kwargs['password'] self._password = kwargs['password']
# database # database
if 'database' in kwargs: if 'database' in kwargs:
self._database = kwargs['database'] self._database = kwargs['database']
@ -43,7 +45,12 @@ class TDengineConnection(object):
self._config = kwargs['config'] self._config = kwargs['config']
self._chandle = CTaosInterface(self._config) self._chandle = CTaosInterface(self._config)
self._conn = self._chandle.connect(self._host, self._user, self._password, self._database, self._port) self._conn = self._chandle.connect(
self._host,
self._user,
self._password,
self._database,
self._port)
def close(self): def close(self):
"""Close current connection. """Close current connection.
@ -55,7 +62,8 @@ class TDengineConnection(object):
""" """
if self._conn is None: if self._conn is None:
return None return None
sub = CTaosInterface.subscribe(self._conn, restart, topic, sql, interval) sub = CTaosInterface.subscribe(
self._conn, restart, topic, sql, interval)
return TDengineSubscription(sub) return TDengineSubscription(sub)
def cursor(self): def cursor(self):
@ -80,7 +88,8 @@ class TDengineConnection(object):
""" """
pass pass
if __name__ == "__main__": if __name__ == "__main__":
conn = TDengineConnection(host='192.168.1.107') conn = TDengineConnection(host='192.168.1.107')
conn.close() conn.close()
print("Hello world") print("Hello world")

View File

@ -3,6 +3,7 @@
from .dbapi import * from .dbapi import *
class FieldType(object): class FieldType(object):
"""TDengine Field Types """TDengine Field Types
""" """
@ -18,13 +19,21 @@ class FieldType(object):
C_BINARY = 8 C_BINARY = 8
C_TIMESTAMP = 9 C_TIMESTAMP = 9
C_NCHAR = 10 C_NCHAR = 10
C_TINYINT_UNSIGNED = 12
C_SMALLINT_UNSIGNED = 13
C_INT_UNSIGNED = 14
C_BIGINT_UNSIGNED = 15
# NULL value definition # NULL value definition
# NOTE: These values should change according to C definition in tsdb.h # NOTE: These values should change according to C definition in tsdb.h
C_BOOL_NULL = 0x02 C_BOOL_NULL = 0x02
C_TINYINT_NULL = -128 C_TINYINT_NULL = -128
C_TINYINT_UNSIGNED_NULL = 255
C_SMALLINT_NULL = -32768 C_SMALLINT_NULL = -32768
C_SMALLINT_UNSIGNED_NULL = 65535
C_INT_NULL = -2147483648 C_INT_NULL = -2147483648
C_INT_UNSIGNED_NULL = 4294967295
C_BIGINT_NULL = -9223372036854775808 C_BIGINT_NULL = -9223372036854775808
C_BIGINT_UNSIGNED_NULL = 18446744073709551615
C_FLOAT_NULL = float('nan') C_FLOAT_NULL = float('nan')
C_DOUBLE_NULL = float('nan') C_DOUBLE_NULL = float('nan')
C_BINARY_NULL = bytearray([int('0xff', 16)]) C_BINARY_NULL = bytearray([int('0xff', 16)])

View File

@ -5,6 +5,7 @@ import threading
# querySeqNum = 0 # querySeqNum = 0
class TDengineCursor(object): class TDengineCursor(object):
"""Database cursor which is used to manage the context of a fetch operation. """Database cursor which is used to manage the context of a fetch operation.
@ -107,8 +108,8 @@ class TDengineCursor(object):
# if threading.get_ident() != self._threadId: # if threading.get_ident() != self._threadId:
# info ="Cursor execute:Thread ID not match,creater:"+str(self._threadId)+" caller:"+str(threading.get_ident()) # info ="Cursor execute:Thread ID not match,creater:"+str(self._threadId)+" caller:"+str(threading.get_ident())
# raise OperationalError(info) # raise OperationalError(info)
# print(info) # print(info)
# return None # return None
if not operation: if not operation:
return None return None
@ -137,8 +138,8 @@ class TDengineCursor(object):
if errno == 0: if errno == 0:
if CTaosInterface.fieldsCount(self._result) == 0: if CTaosInterface.fieldsCount(self._result) == 0:
self._affected_rows += CTaosInterface.affectedRows( self._affected_rows += CTaosInterface.affectedRows(
self._result ) self._result)
return CTaosInterface.affectedRows(self._result ) return CTaosInterface.affectedRows(self._result)
else: else:
self._fields = CTaosInterface.useResult( self._fields = CTaosInterface.useResult(
self._result) self._result)
@ -168,11 +169,26 @@ class TDengineCursor(object):
if (dataType.upper() == "TINYINT"): if (dataType.upper() == "TINYINT"):
if (self._description[col][1] == FieldType.C_TINYINT): if (self._description[col][1] == FieldType.C_TINYINT):
return True return True
if (dataType.upper() == "TINYINT UNSIGNED"):
if (self._description[col][1] == FieldType.C_TINYINT_UNSIGNED):
return True
if (dataType.upper() == "SMALLINT"):
if (self._description[col][1] == FieldType.C_SMALLINT):
return True
if (dataType.upper() == "SMALLINT UNSIGNED"):
if (self._description[col][1] == FieldType.C_SMALLINT_UNSIGNED):
return True
if (dataType.upper() == "INT"): if (dataType.upper() == "INT"):
if (self._description[col][1] == FieldType.C_INT): if (self._description[col][1] == FieldType.C_INT):
return True return True
if (dataType.upper() == "INT UNSIGNED"):
if (self._description[col][1] == FieldType.C_INT_UNSIGNED):
return True
if (dataType.upper() == "BIGINT"): if (dataType.upper() == "BIGINT"):
if (self._description[col][1] == FieldType.C_INT): if (self._description[col][1] == FieldType.C_BIGINT):
return True
if (dataType.upper() == "BIGINT UNSIGNED"):
if (self._description[col][1] == FieldType.C_BIGINT_UNSIGNED):
return True return True
if (dataType.upper() == "FLOAT"): if (dataType.upper() == "FLOAT"):
if (self._description[col][1] == FieldType.C_FLOAT): if (self._description[col][1] == FieldType.C_FLOAT):
@ -201,10 +217,13 @@ class TDengineCursor(object):
buffer = [[] for i in range(len(self._fields))] buffer = [[] for i in range(len(self._fields))]
self._rowcount = 0 self._rowcount = 0
while True: while True:
block, num_of_fields = CTaosInterface.fetchRow(self._result, self._fields) block, num_of_fields = CTaosInterface.fetchRow(
self._result, self._fields)
errno = CTaosInterface.libtaos.taos_errno(self._result) errno = CTaosInterface.libtaos.taos_errno(self._result)
if errno != 0: if errno != 0:
raise ProgrammingError(CTaosInterface.errStr(self._result), errno) raise ProgrammingError(
CTaosInterface.errStr(
self._result), errno)
if num_of_fields == 0: if num_of_fields == 0:
break break
self._rowcount += num_of_fields self._rowcount += num_of_fields
@ -219,15 +238,20 @@ class TDengineCursor(object):
buffer = [[] for i in range(len(self._fields))] buffer = [[] for i in range(len(self._fields))]
self._rowcount = 0 self._rowcount = 0
while True: while True:
block, num_of_fields = CTaosInterface.fetchBlock(self._result, self._fields) block, num_of_fields = CTaosInterface.fetchBlock(
self._result, self._fields)
errno = CTaosInterface.libtaos.taos_errno(self._result) errno = CTaosInterface.libtaos.taos_errno(self._result)
if errno != 0: if errno != 0:
raise ProgrammingError(CTaosInterface.errStr(self._result), errno) raise ProgrammingError(
if num_of_fields == 0: break CTaosInterface.errStr(
self._result), errno)
if num_of_fields == 0:
break
self._rowcount += num_of_fields self._rowcount += num_of_fields
for i in range(len(self._fields)): for i in range(len(self._fields)):
buffer[i].extend(block[i]) buffer[i].extend(block[i])
return list(map(tuple, zip(*buffer))) return list(map(tuple, zip(*buffer)))
def nextset(self): def nextset(self):
""" """
""" """
@ -259,8 +283,8 @@ class TDengineCursor(object):
# if threading.get_ident() != self._threadId: # if threading.get_ident() != self._threadId:
# info = "Cursor handleresult:Thread ID not match,creater:"+str(self._threadId)+" caller:"+str(threading.get_ident()) # info = "Cursor handleresult:Thread ID not match,creater:"+str(self._threadId)+" caller:"+str(threading.get_ident())
# raise OperationalError(info) # raise OperationalError(info)
# print(info) # print(info)
# return None # return None
self._description = [] self._description = []
for ele in self._fields: for ele in self._fields:
@ -268,4 +292,3 @@ class TDengineCursor(object):
(ele['name'], ele['type'], None, None, None, None, False)) (ele['name'], ele['type'], None, None, None, None, False))
return self._result return self._result

View File

@ -4,6 +4,7 @@
import time import time
import datetime import datetime
class DBAPITypeObject(object): class DBAPITypeObject(object):
def __init__(self, *values): def __init__(self, *values):
self.values = values self.values = values
@ -16,23 +17,28 @@ class DBAPITypeObject(object):
else: else:
return -1 return -1
Date = datetime.date Date = datetime.date
Time = datetime.time Time = datetime.time
Timestamp = datetime.datetime Timestamp = datetime.datetime
def DataFromTicks(ticks): def DataFromTicks(ticks):
return Date(*time.localtime(ticks)[:3]) return Date(*time.localtime(ticks)[:3])
def TimeFromTicks(ticks): def TimeFromTicks(ticks):
return Time(*time.localtime(ticks)[3:6]) return Time(*time.localtime(ticks)[3:6])
def TimestampFromTicks(ticks): def TimestampFromTicks(ticks):
return Timestamp(*time.localtime(ticks)[:6]) return Timestamp(*time.localtime(ticks)[:6])
Binary = bytes Binary = bytes
# STRING = DBAPITypeObject(*constants.FieldType.get_string_types()) # STRING = DBAPITypeObject(*constants.FieldType.get_string_types())
# BINARY = DBAPITypeObject(*constants.FieldType.get_binary_types()) # BINARY = DBAPITypeObject(*constants.FieldType.get_binary_types())
# NUMBER = BAPITypeObject(*constants.FieldType.get_number_types()) # NUMBER = BAPITypeObject(*constants.FieldType.get_number_types())
# DATETIME = DBAPITypeObject(*constants.FieldType.get_timestamp_types()) # DATETIME = DBAPITypeObject(*constants.FieldType.get_timestamp_types())
# ROWID = DBAPITypeObject() # ROWID = DBAPITypeObject()

View File

@ -1,35 +1,41 @@
"""Python exceptions """Python exceptions
""" """
class Error(Exception): class Error(Exception):
def __init__(self, msg=None, errno=None): def __init__(self, msg=None, errno=None):
self.msg = msg self.msg = msg
self._full_msg = self.msg self._full_msg = self.msg
self.errno = errno self.errno = errno
def __str__(self): def __str__(self):
return self._full_msg return self._full_msg
class Warning(Exception): class Warning(Exception):
"""Exception raised for important warnings like data truncations while inserting. """Exception raised for important warnings like data truncations while inserting.
""" """
pass pass
class InterfaceError(Error): class InterfaceError(Error):
"""Exception raised for errors that are related to the database interface rather than the database itself. """Exception raised for errors that are related to the database interface rather than the database itself.
""" """
pass pass
class DatabaseError(Error): class DatabaseError(Error):
"""Exception raised for errors that are related to the database. """Exception raised for errors that are related to the database.
""" """
pass pass
class DataError(DatabaseError): class DataError(DatabaseError):
"""Exception raised for errors that are due to problems with the processed data like division by zero, numeric value out of range. """Exception raised for errors that are due to problems with the processed data like division by zero, numeric value out of range.
""" """
pass pass
class OperationalError(DatabaseError): class OperationalError(DatabaseError):
"""Exception raised for errors that are related to the database's operation and not necessarily under the control of the programmer """Exception raised for errors that are related to the database's operation and not necessarily under the control of the programmer
""" """
@ -41,17 +47,20 @@ class IntegrityError(DatabaseError):
""" """
pass pass
class InternalError(DatabaseError): class InternalError(DatabaseError):
"""Exception raised when the database encounters an internal error. """Exception raised when the database encounters an internal error.
""" """
pass pass
class ProgrammingError(DatabaseError): class ProgrammingError(DatabaseError):
"""Exception raised for programming errors. """Exception raised for programming errors.
""" """
pass pass
class NotSupportedError(DatabaseError): class NotSupportedError(DatabaseError):
"""Exception raised in case a method or database API was used which is not supported by the database,. """Exception raised in case a method or database API was used which is not supported by the database,.
""" """
pass pass

View File

@ -1,32 +1,33 @@
from .cinterface import CTaosInterface from .cinterface import CTaosInterface
from .error import * from .error import *
class TDengineSubscription(object): class TDengineSubscription(object):
"""TDengine subscription object """TDengine subscription object
""" """
def __init__(self, sub): def __init__(self, sub):
self._sub = sub self._sub = sub
def consume(self): def consume(self):
"""Consume rows of a subscription """Consume rows of a subscription
""" """
if self._sub is None: if self._sub is None:
raise OperationalError("Invalid use of consume") raise OperationalError("Invalid use of consume")
result, fields = CTaosInterface.consume(self._sub) result, fields = CTaosInterface.consume(self._sub)
buffer = [[] for i in range(len(fields))] buffer = [[] for i in range(len(fields))]
while True: while True:
block, num_of_fields = CTaosInterface.fetchBlock(result, fields) block, num_of_fields = CTaosInterface.fetchBlock(result, fields)
if num_of_fields == 0: break if num_of_fields == 0:
break
for i in range(len(fields)): for i in range(len(fields)):
buffer[i].extend(block[i]) buffer[i].extend(block[i])
self.fields = fields self.fields = fields
return list(map(tuple, zip(*buffer))) return list(map(tuple, zip(*buffer)))
def close(self, keepProgress=True):
def close(self, keepProgress = True):
"""Close the Subscription. """Close the Subscription.
""" """
if self._sub is None: if self._sub is None:
@ -38,15 +39,19 @@ class TDengineSubscription(object):
if __name__ == '__main__': if __name__ == '__main__':
from .connection import TDengineConnection from .connection import TDengineConnection
conn = TDengineConnection(host="127.0.0.1", user="root", password="taosdata", database="test") conn = TDengineConnection(
host="127.0.0.1",
user="root",
password="taosdata",
database="test")
# Generate a cursor object to run SQL commands # Generate a cursor object to run SQL commands
sub = conn.subscribe(True, "test", "select * from meters;", 1000) sub = conn.subscribe(True, "test", "select * from meters;", 1000)
for i in range(0,10): for i in range(0, 10):
data = sub.consume() data = sub.consume()
for d in data: for d in data:
print(d) print(d)
sub.close() sub.close()
conn.close() conn.close()

View File

@ -3,7 +3,6 @@ from .connection import TDengineConnection
from .cursor import TDengineCursor from .cursor import TDengineCursor
# Globals # Globals
apilevel = '2.0.3'
threadsafety = 0 threadsafety = 0
paramstyle = 'pyformat' paramstyle = 'pyformat'
@ -21,4 +20,4 @@ def connect(*args, **kwargs):
@rtype: TDengineConnector @rtype: TDengineConnector
""" """
return TDengineConnection(*args, **kwargs) return TDengineConnection(*args, **kwargs)

View File

@ -3,7 +3,6 @@ from .connection import TDengineConnection
from .cursor import TDengineCursor from .cursor import TDengineCursor
# Globals # Globals
apilevel = '2.0.3'
threadsafety = 0 threadsafety = 0
paramstyle = 'pyformat' paramstyle = 'pyformat'
@ -21,4 +20,4 @@ def connect(*args, **kwargs):
@rtype: TDengineConnector @rtype: TDengineConnector
""" """
return TDengineConnection(*args, **kwargs) return TDengineConnection(*args, **kwargs)

View File

@ -0,0 +1,56 @@
###################################################################
# Copyright (c) 2016 by TAOS Technologies, Inc.
# All rights reserved.
#
# This file is proprietary and confidential to TAOS Technologies.
# No part of this file may be reproduced, stored, transmitted,
# disclosed or used in any form or by any means other than as
# expressly provided by the written permission from Jianhui Tao
#
###################################################################
# -*- coding: utf-8 -*-
import sys
from util.log import *
from util.cases import *
from util.sql import *
class TDTestCase:
def init(self, conn, logSql):
tdLog.debug("start to execute %s" % __file__)
tdSql.init(conn.cursor(), logSql)
def run(self):
tdSql.prepare()
ret = tdSql.execute(
'create table tb (ts timestamp, speed int unsigned)')
insertRows = 10
tdLog.info("insert %d rows" % (insertRows))
for i in range(0, insertRows):
ret = tdSql.execute(
'insert into tb values (now + %dm, %d)' %
(i, i))
tdLog.info("insert earlier data")
tdSql.execute('insert into tb values (now - 5m , 10)')
tdSql.execute('insert into tb values (now - 6m , 10)')
tdSql.execute('insert into tb values (now - 7m , 10)')
tdSql.execute('insert into tb values (now - 8m , 4294967294)')
tdSql.error('insert into tb values (now - 9m, -1)')
tdSql.error('insert into tb values (now - 9m, 4294967295)')
tdSql.query("select * from tb")
tdSql.checkRows(insertRows + 4)
def stop(self):
tdSql.close()
tdLog.success("%s successfully executed" % __file__)
tdCases.addWindows(__file__, TDTestCase())
tdCases.addLinux(__file__, TDTestCase())