├── taos ├── _version.py ├── precision.py ├── schemaless.py ├── log.py ├── subscription.py ├── __init__.py ├── constants.py ├── bind_base.py ├── statement.py └── error.py ├── taos-ws-py ├── .env ├── sh │ ├── test.sh │ └── build.sh ├── build.rs ├── .gitignore ├── tests │ ├── decorators.py │ ├── test_cursor.py │ ├── test_cloud.py │ ├── test_cursor_fetchmany.py │ ├── test_conn.py │ ├── test_consumer.py │ ├── test_schemaless.py │ ├── test_tmq_assignment.py │ ├── test_meters.py │ ├── test_cursor_geo.py │ ├── test_stmt2.py │ ├── test_stmt.py │ ├── test_req_id.py │ ├── test_tmq.py │ └── test_blob.py ├── examples │ ├── demo.py │ ├── cursor_execute.py │ ├── connection_execute.py │ ├── connection_execute_with_req_id.py │ ├── cursor_execute_with_req_id.py │ ├── schemaless_example.py │ └── stmt_example.py ├── Cargo.toml ├── src │ └── field.rs ├── dev.md └── CHANGELOG.md ├── tests ├── utils.py ├── decorators.py ├── test_parse_date.py ├── test_version.py ├── test_connection.py ├── test_exception.py ├── test_req_id.py ├── test_info.py ├── test_vgroup_id.py ├── test_native_cursor.py ├── test_cursor_logfile.py ├── test_rest_api.py ├── test_query_blob.py ├── test-stmt-insert-multi-nullable.py ├── test_connect_args.py ├── test_native_many.py ├── test_decode_binary.py ├── test_sqlalchemy_conntect_pool.py ├── test_bind_base.py ├── test_subscribe.py ├── test_query_a.py └── test_dbutils_conntect_pool.py ├── .github └── workflows │ ├── taos.cfg │ ├── pypi.yml │ ├── taos-ws-py-compatibility.yml │ └── release.yml ├── examples ├── pep-249.py ├── rest_cursor.py ├── query-objectively.py ├── pandas-read-sql-tdengine-cloud.py ├── pandas-read-sql.py ├── rest_client.py ├── rest_client_example.py ├── rest_client_with_req_id_example.py ├── json-tag.py ├── demo.py ├── insert-lines.py ├── connect_cloud_service.py ├── import-json.py ├── trades-to-taos.py ├── connect_websocket_examples.py ├── connect_websocket_with_req_id_examples.py ├── cursor_usage_native_reference.py ├── cursor_usage_native_reference_with_req_id.py ├── result_set_examples.py ├── result_set_with_req_id_examples.py ├── v2 │ ├── subscribe-async.py │ └── subscribe-sync.py ├── bind-row.py ├── bind-multi.py ├── tmq_consumer.py ├── connection_usage_native_reference.py ├── connection_usage_native_reference_with_req_id.py ├── rest_query.py ├── query-async.py ├── cursor_execute_many.py ├── connect_rest_examples.py ├── connect_rest_with_req_id_examples.py ├── tmq_assignment.py ├── native_all_type_query.py ├── ws_all_type_query.py ├── native_pandas_example.py └── ws_all_type_stmt.py ├── test_taospy.sh ├── ci ├── extract-changelog.sh ├── build-doc.sh ├── publish.sh ├── release.sh ├── release-ws.sh └── changelog-generate.sh ├── test_taos-ws-py.sh ├── .pre-commit-config.yaml ├── CONTRIBUTING.md ├── dev.md ├── LICENSE ├── taosrest ├── errors.py ├── sqlalchemy.py ├── __init__.py └── cursor.py ├── pyproject.toml ├── .gitignore └── README-CN.md /taos/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = "2.8.6" 2 | -------------------------------------------------------------------------------- /taos-ws-py/.env: -------------------------------------------------------------------------------- 1 | export TDENGINE_URL=localhost:6041 2 | -------------------------------------------------------------------------------- /taos-ws-py/sh/test.sh: -------------------------------------------------------------------------------- 1 | python3 -m pytest tests/test_req_id.py 2 | -------------------------------------------------------------------------------- /taos-ws-py/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | shadow_rs::new().unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /tests/utils.py: -------------------------------------------------------------------------------- 1 | def tear_down_database(conn, db_name): 2 | conn.execute("DROP DATABASE IF EXISTS %s" % db_name) 3 | -------------------------------------------------------------------------------- /taos-ws-py/sh/build.sh: -------------------------------------------------------------------------------- 1 | python3 -m maturin build --strip && pip3 install ./target/wheels/taos_ws_py-0.2.4-cp37-abi3-manylinux_2_31_x86_64.whl --force-reinstall 2 | -------------------------------------------------------------------------------- /.github/workflows/taos.cfg: -------------------------------------------------------------------------------- 1 | fqdn localhost 2 | firstEp localhost:6030 3 | supportVnodes 256 4 | dataDir /tmp/taos/v3/data 5 | logDir /tmp/taos/v3/log 6 | debugFlag 135 7 | -------------------------------------------------------------------------------- /examples/pep-249.py: -------------------------------------------------------------------------------- 1 | import taos 2 | 3 | conn = taos.connect() 4 | cursor = conn.cursor() 5 | 6 | cursor.execute("show databases") 7 | results = cursor.fetchall() 8 | for row in results: 9 | print(row) 10 | 11 | conn.close() 12 | -------------------------------------------------------------------------------- /taos-ws-py/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | USER_MANUAL.md TODO.md 4 | TDengine 5 | .vscode 6 | /conf 7 | /docs 8 | cobertura.xml 9 | lcov.info 10 | tarpaulin-report.html 11 | tarpaulin-report.json 12 | rust-toolchain 13 | sync.sh 14 | .cargo 15 | .cache-cargo 16 | default.profraw 17 | -------------------------------------------------------------------------------- /taos/precision.py: -------------------------------------------------------------------------------- 1 | class PrecisionEnum(object): 2 | """Precision enums""" 3 | 4 | Milliseconds = 0 5 | Microseconds = 1 6 | Nanoseconds = 2 7 | 8 | 9 | class PrecisionError(Exception): 10 | """Python datetime does not support nanoseconds error""" 11 | 12 | pass 13 | -------------------------------------------------------------------------------- /test_taospy.sh: -------------------------------------------------------------------------------- 1 | pip3 install poetry==1.8.5 2 | poetry install --no-interaction --with=dev 3 | poetry run pip install "numpy<2.0.0" 4 | export TDENGINE_URL=localhost:6041 5 | curl -L -H "Authorization: Basic cm9vdDp0YW9zZGF0YQ==" -d "show databases" localhost:6041/rest/sql 6 | poetry run pytest ./tests 7 | 8 | -------------------------------------------------------------------------------- /ci/extract-changelog.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | lastv=$(grep -n '## v' CHANGELOG.md |cut -f1 -d:|head -n2 |tail -n1) 3 | thisv=$(grep -n '## v' CHANGELOG.md |cut -f1 -d:|head -n1) 4 | 5 | if [ $lastv = $thisv ]; then 6 | cat CHANGELOG.md | tail -n+$thisv 7 | else 8 | sed -n "$thisv,$(expr $lastv - 1)p" CHANGELOG.md 9 | fi -------------------------------------------------------------------------------- /ci/build-doc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pip3 install pdoc3 3 | [ -e .env ] && source .env 4 | pdoc --html --force --output-dir ../docs taosrest 5 | pdoc --html --force --output-dir ../docs taos 6 | # copy generated html files to Document Server 7 | sshpass -p "$PASSWORD" scp -r ../docs/* root@$DOC_HOST:/data/apidocs/taospy/ 8 | -------------------------------------------------------------------------------- /test_taos-ws-py.sh: -------------------------------------------------------------------------------- 1 | pip3 install poetry==1.8.5 2 | poetry install --no-interaction --with=dev 3 | poetry run pip install "numpy<2.0.0" taos-ws-py 4 | export TDENGINE_URL=localhost:6041 5 | curl -L -H "Authorization: Basic cm9vdDp0YW9zZGF0YQ==" -d "show databases" localhost:6041/rest/sql 6 | poetry run pytest ./taos-ws-py/tests/ 7 | -------------------------------------------------------------------------------- /examples/rest_cursor.py: -------------------------------------------------------------------------------- 1 | import taosrest 2 | 3 | # all parameters are optional 4 | conn = taosrest.connect(url="http://localhost:6041", user="root", password="taosdata") 5 | cursor = conn.cursor() 6 | 7 | cursor.execute("show databases") 8 | results: list[tuple] = cursor.fetchall() 9 | for row in results: 10 | print(row) 11 | -------------------------------------------------------------------------------- /examples/query-objectively.py: -------------------------------------------------------------------------------- 1 | import taos 2 | 3 | conn = taos.connect() 4 | conn.execute("create database if not exists pytest") 5 | 6 | result = conn.query("show databases") 7 | num_of_fields = result.field_count 8 | for field in result.fields: 9 | print(field) 10 | for row in result: 11 | print(row) 12 | conn.execute("drop database pytest") 13 | -------------------------------------------------------------------------------- /tests/decorators.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | import os 3 | 4 | 5 | class NoEnvError(Exception): 6 | pass 7 | 8 | 9 | def check_env(func): 10 | @wraps(func) 11 | def _func(): 12 | if "TDENGINE_URL" not in os.environ: 13 | raise NoEnvError("Please set environment variable TDENGINE_URL") 14 | func() 15 | 16 | return _func 17 | -------------------------------------------------------------------------------- /taos-ws-py/tests/decorators.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | import os 3 | 4 | 5 | class NoEnvError(Exception): 6 | pass 7 | 8 | 9 | def check_env(func): 10 | @wraps(func) 11 | def _func(): 12 | if "TDENGINE_URL" not in os.environ: 13 | raise NoEnvError("Please set environment variable TDENGINE_URL") 14 | func() 15 | 16 | return _func 17 | -------------------------------------------------------------------------------- /examples/pandas-read-sql-tdengine-cloud.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pandas 3 | from sqlalchemy import create_engine, text 4 | 5 | url = os.environ["TDENGINE_CLOUD_URL"] 6 | token = os.environ["TDENGINE_CLOUD_TOKEN"] 7 | url = url.replace("https", "") 8 | 9 | engine = create_engine(f"taosws{url}?token={token}") 10 | conn = engine.connect() 11 | res = pandas.read_sql(text("show databases"), conn) 12 | conn.close() 13 | print(res) 14 | -------------------------------------------------------------------------------- /examples/pandas-read-sql.py: -------------------------------------------------------------------------------- 1 | import pandas 2 | from sqlalchemy import create_engine, text 3 | 4 | protocol_and_port = {"taos": 6030, "taosrest": 6041, "taosws": 6041} 5 | 6 | for (protocol, port) in protocol_and_port.items(): 7 | engine = create_engine(f"{protocol}://root:taosdata@localhost") 8 | conn = engine.connect() 9 | res = pandas.read_sql(text("show databases"), conn) 10 | conn.close() 11 | print(res) 12 | -------------------------------------------------------------------------------- /examples/rest_client.py: -------------------------------------------------------------------------------- 1 | from taosrest import RestClient 2 | import os 3 | 4 | # connect to cloud 5 | url = os.environ["TDENGINE_CLOUD_URL"] 6 | token = os.environ["TDENGINE_CLOUD_TOKEN"] 7 | 8 | cloud_client = RestClient(url=url, token=token) 9 | resp = cloud_client.sql("show databases") 10 | print(resp) 11 | 12 | try: 13 | resp = cloud_client.sql("describe test.noexits") 14 | print(resp) 15 | except BaseException as e: 16 | print(e) 17 | -------------------------------------------------------------------------------- /ci/publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | rm -rf dist 4 | old=$(git rev-parse --abbrev-ref HEAD) 5 | poetry publish --build -u "${PYPI_USER}" -p "${PYPI_PASS}" || true 6 | version=$(ls -rt dist/*.tar.gz| tail -n1|sed -E 's/.*taospy-//;s/.tar.gz$//') 7 | git checkout release 8 | tar --strip-components=1 -xvf dist/*.tar.gz 9 | tar -tf dist/*.tar.gz |sed -E 's#[^/]+/##'|xargs -i git add '{}' -f 10 | git commit -m "release: $version" 11 | git checkout $old 12 | -------------------------------------------------------------------------------- /examples/rest_client_example.py: -------------------------------------------------------------------------------- 1 | from taosrest import RestClient 2 | 3 | client = RestClient("http://localhost:6041", user="root", password="taosdata") 4 | res: dict = client.sql("SELECT ts, current FROM power.meters LIMIT 1") 5 | print(res) 6 | 7 | # output: 8 | # {'status': 'succ', 'head': ['ts', 'current'], 'column_meta': [['ts', 9, 8], ['current', 6, 4]], 'data': [[datetime.datetime(2018, 10, 3, 14, 38, 5, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800), '+08:00')), 10.3]], 'rows': 1} 9 | -------------------------------------------------------------------------------- /taos/schemaless.py: -------------------------------------------------------------------------------- 1 | class SmlPrecision: 2 | """Schemaless timestamp precision constants""" 3 | 4 | NOT_CONFIGURED = 0 # C.TSDB_SML_TIMESTAMP_NOT_CONFIGURED 5 | HOURS = 1 6 | MINUTES = 2 7 | SECONDS = 3 8 | MILLI_SECONDS = 4 9 | MICRO_SECONDS = 5 10 | NANO_SECONDS = 6 11 | 12 | 13 | class SmlProtocol: 14 | """Schemaless protocol constants""" 15 | 16 | UNKNOWN_PROTOCOL = 0 17 | LINE_PROTOCOL = 1 18 | TELNET_PROTOCOL = 2 19 | JSON_PROTOCOL = 3 20 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v2.3.0 4 | hooks: 5 | - id: check-yaml 6 | - id: check-json 7 | - id: end-of-file-fixer 8 | - id: trailing-whitespace 9 | 10 | repos: 11 | - repo: https://github.com/psf/black 12 | rev: stable 13 | hooks: 14 | - id: black 15 | 16 | repos: 17 | - repo: https://github.com/crate-ci/typos 18 | rev: v1.15.7 19 | hooks: 20 | - id: typos 21 | 22 | -------------------------------------------------------------------------------- /examples/rest_client_with_req_id_example.py: -------------------------------------------------------------------------------- 1 | from taosrest import RestClient 2 | 3 | client = RestClient("http://localhost:6041", user="root", password="taosdata") 4 | res: dict = client.sql("SELECT ts, current FROM power.meters LIMIT 1", req_id=1) 5 | print(res) 6 | 7 | # output: 8 | # {'status': 'succ', 'head': ['ts', 'current'], 'column_meta': [['ts', 9, 8], ['current', 6, 4]], 'data': [[datetime.datetime(2018, 10, 3, 14, 38, 5, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800), '+08:00')), 10.3]], 'rows': 1} 9 | -------------------------------------------------------------------------------- /tests/test_parse_date.py: -------------------------------------------------------------------------------- 1 | from iso8601 import parse_date 2 | 3 | 4 | def test_parse_time(): 5 | s1 = "2018-10-03T14:38:15+08:00" 6 | t1 = parse_date(s1) 7 | print("\n", repr(t1)) 8 | assert t1.isoformat() == "2018-10-03T14:38:15+08:00" 9 | s2 = "2018-10-03T14:38:16.8+08:00" 10 | t2 = parse_date(s2) 11 | assert t2.isoformat() == "2018-10-03T14:38:16.800000+08:00" 12 | s3 = "2018-10-03T14:38:16.65+08:00" 13 | t3 = parse_date(s3) 14 | assert t3.isoformat() == "2018-10-03T14:38:16.650000+08:00" 15 | -------------------------------------------------------------------------------- /tests/test_version.py: -------------------------------------------------------------------------------- 1 | import toml 2 | from pathlib import Path 3 | import taos 4 | 5 | 6 | def test_versions_are_in_sync(): 7 | """Checks if the pyproject.toml and package.__init__.py __version__ are in sync.""" 8 | 9 | path = Path(__file__).resolve().parents[1] / "pyproject.toml" 10 | pyproject = toml.loads(open(str(path), encoding="utf-8").read()) 11 | pyproject_version = pyproject["tool"]["poetry"]["version"] 12 | 13 | package_init_version = taos.__version__ 14 | 15 | assert package_init_version == pyproject_version 16 | -------------------------------------------------------------------------------- /.github/workflows/pypi.yml: -------------------------------------------------------------------------------- 1 | on: 2 | release: 3 | types: 4 | - published 5 | 6 | jobs: 7 | publish: 8 | runs-on: ubuntu-22.04 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v1 12 | - name: Poetry Setup 13 | uses: abatilo/actions-poetry@v2 14 | with: 15 | poetry-version: 1.2 16 | 17 | - name: Publish to PyPI 18 | env: 19 | PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} 20 | run: | 21 | pip3 install --upgrade requests 22 | poetry config pypi-token.pypi $PYPI_TOKEN 23 | poetry publish --build 24 | -------------------------------------------------------------------------------- /tests/test_connection.py: -------------------------------------------------------------------------------- 1 | # encoding:UTF-8 2 | from dotenv import load_dotenv 3 | 4 | from taos.cinterface import * 5 | import taos 6 | from taos.connection import * 7 | 8 | load_dotenv() 9 | 10 | 11 | def test_default_connect(): 12 | if not taos.IS_V3: 13 | return 14 | # 15 | conn = TaosConnection() 16 | conn.close() 17 | print("pass test_default_connect") 18 | 19 | 20 | def test_stmt2_invalid_conn(): 21 | if not taos.IS_V3: 22 | return 23 | # 24 | conn = TaosConnection() 25 | conn.close() 26 | stmt2 = conn.statement2() 27 | assert stmt2 is None 28 | print("pass test_stmt2_invalid_conn") 29 | -------------------------------------------------------------------------------- /examples/json-tag.py: -------------------------------------------------------------------------------- 1 | # encoding:UTF-8 2 | import taos 3 | 4 | conn = taos.connect() 5 | conn.execute("drop database if exists py_test_json_type") 6 | conn.execute("create database if not exists py_test_json_type") 7 | conn.execute("use py_test_json_type") 8 | 9 | conn.execute("create stable s1 (ts timestamp, v1 int) tags (info json)") 10 | conn.execute('create table s1_1 using s1 tags (\'{"k1": "v1"}\')') 11 | tags = conn.query("select info, tbname from s1").fetch_all_into_dict() 12 | print(tags) 13 | 14 | k1 = conn.query("select info->'k1' as k1 from s1").fetch_all_into_dict() 15 | print(k1) 16 | 17 | conn.execute("drop database py_test_json_type") 18 | 19 | conn.close() 20 | -------------------------------------------------------------------------------- /tests/test_exception.py: -------------------------------------------------------------------------------- 1 | import taos 2 | 3 | 4 | def test_invalid_dbname(): 5 | conn: taos.TaosConnection = None 6 | try: 7 | conn = taos.connect(database="notexist") 8 | except taos.Error as e: 9 | print("\n=============================") 10 | print(e) 11 | print("exception class: ", e.__class__.__name__) # ConnectionError 12 | print("error number:", e.errno) # 65535 13 | print("error message:", e.msg) # connect to TDengine failed 14 | except BaseException as other: 15 | print("exception occur") 16 | print(other) 17 | finally: 18 | if conn is not None: 19 | conn.close() 20 | -------------------------------------------------------------------------------- /tests/test_req_id.py: -------------------------------------------------------------------------------- 1 | from taos import * 2 | 3 | 4 | def test_gen_req_id(): 5 | req_id = utils.gen_req_id() 6 | print(req_id) 7 | print(hex(req_id)) 8 | 9 | 10 | def test_murmurhash3_32_tail_length_1(): 11 | case1 = "a".encode("utf-8") 12 | hashed = utils.murmurhash3_32(case1, len(case1)) 13 | print(hashed) 14 | 15 | 16 | def test_murmurhash3_32_tail_length_2(): 17 | case2 = "python".encode("utf-8") 18 | hashed = utils.murmurhash3_32(case2, len(case2)) 19 | print(hashed) 20 | 21 | 22 | def test_murmurhash3_32_tail_length_3(): 23 | case3 = "123".encode("utf-8") 24 | hashed = utils.murmurhash3_32(case3, len(case3)) 25 | print(hashed) 26 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Dev Env 4 | 5 | ```bash 6 | pip3 install poetry 7 | ``` 8 | 9 | ## Release 10 | 11 | Before release, you use must the `PYPI_TOKEN` environment to satisfy the `pip3 publish` requires. 12 | 13 | For taosws: 14 | 15 | ```bash 16 | export PYPI_TOKEN=xxx 17 | ./ci/release-ws.sh 18 | ``` 19 | 20 | For taospy: 21 | 22 | ```bash 23 | ./ci/release.sh 24 | ``` 25 | 26 | Release script will do: 27 | 28 | 1. Generate changelog. 29 | 2. Commit with changelog and version bump. 30 | 3. Tag with new version 31 | 4. Push commit directly to main branch. 32 | 5. Push tags 33 | 34 | Note that taosws and taospy use different versions. 35 | -------------------------------------------------------------------------------- /ci/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ci=$(realpath $(dirname $0)) 3 | newv=$1 4 | if [ "$newv" = "" ]; then 5 | echo "$0 " 6 | exit 1 7 | fi 8 | echo "__version__ = '$newv'" > taos/_version.py 9 | sed -E '3s#version.*#version = "'$newv'"#' pyproject.toml >> pyproject.toml2 10 | mv pyproject.toml2 pyproject.toml 11 | 12 | sed -n "1,9p" CHANGELOG.md > CHANGELOG.md2 13 | printf "## v$newv - $(date +%F)\n\n" >> CHANGELOG.md2 14 | $ci/changelog-generate.sh >> CHANGELOG.md2 15 | sed "1,9d" CHANGELOG.md >> CHANGELOG.md2 16 | mv CHANGELOG.md2 CHANGELOG.md 17 | 18 | git commit -a -m "release: v$newv" 19 | git push 20 | git tag v$newv 21 | git push --force origin v$newv:v$newv 22 | #bash build_doc.sh 23 | -------------------------------------------------------------------------------- /taos-ws-py/tests/test_cursor.py: -------------------------------------------------------------------------------- 1 | import taosws 2 | import datetime 3 | 4 | conn = taosws.connect() 5 | cursor = conn.cursor() 6 | 7 | cursor.execute("select 1") 8 | db = "t_ws" 9 | cursor.execute("drop database if exists {}", db) 10 | cursor.execute("create database {}", db) 11 | cursor.execute("use {name}", name=db) 12 | cursor.execute("create stable stb (ts timestamp, v1 int) tags(t1 int)") 13 | 14 | data = [{"name": "tb1", "t1": 1}, {"name": "tb2", "t1": 2}] 15 | cursor.execute_many("create table {name} using stb tags({t1})", data) 16 | 17 | ts = datetime.datetime.now().astimezone() 18 | data = [("tb1", ts, 1), ("tb2", ts, 2)] 19 | cursor.execute_many("insert into {} values('{}', {})", data) 20 | -------------------------------------------------------------------------------- /ci/release-ws.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | ci=$(realpath $(dirname $0)) 4 | newv=$1 5 | if [ "$newv" = "" ]; then 6 | echo "$0 " 7 | exit 1 8 | fi 9 | cd taos-ws-py 10 | sed -E '3s#version.*#version = "'$newv'"#' Cargo.toml >> Cargo.toml2 11 | mv Cargo.toml2 Cargo.toml 12 | 13 | sed -n "1,9p" CHANGELOG.md > CHANGELOG.md2 14 | printf "## v$newv - $(date +%F)\n\n" >> CHANGELOG.md2 15 | $ci/changelog-generate.sh . taos-ws-py >> CHANGELOG.md2 16 | sed "1,9d" CHANGELOG.md >> CHANGELOG.md2 17 | mv CHANGELOG.md2 CHANGELOG.md 18 | 19 | # git commit -a -m "release(taos-ws-py): v$newv" 20 | #git push 21 | #git tag taos-ws-py-v$newv 22 | #git push --force origin taos-ws-py-v$newv:taos-ws-py-v$newv 23 | -------------------------------------------------------------------------------- /taos-ws-py/tests/test_cloud.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import taosws 3 | 4 | 5 | def taos_ws_connect(token): 6 | print("-" * 40) 7 | print("test_ws_connect") 8 | conn = taosws.connect("%s?token=%s" % ("wss://gw.cloud.taosdata.com", token)) 9 | res = conn.query_with_req_id("show databases", 1) 10 | dbs = [row[0] for row in res] 11 | print(dbs) 12 | # check sys db exist 13 | assert "information_schema" in dbs 14 | assert "performance_schema" in dbs 15 | print("test_ws_connect done") 16 | print("-" * 40) 17 | 18 | 19 | if __name__ == "__main__": 20 | if len(sys.argv) < 2: 21 | print("need token argument passed.\n") 22 | else: 23 | taos_ws_connect(sys.argv[1]) 24 | -------------------------------------------------------------------------------- /examples/demo.py: -------------------------------------------------------------------------------- 1 | import taos 2 | 3 | conn = taos.connect( 4 | host="127.0.0.1", user="root", password="taosdata", database="", config="/etc/taos", timezone="Asia/Shanghai" 5 | ) 6 | cursor = conn.cursor() 7 | 8 | sql = "drop database if exists db" 9 | cursor.execute(sql) 10 | sql = "create database if not exists db" 11 | cursor.execute(sql) 12 | sql = "create table db.tb(ts timestamp, n int, bin binary(10), nc nchar(10))" 13 | cursor.execute(sql) 14 | sql = "insert into db.tb values (1650000000000, 1, 'abc', '北京')" 15 | cursor.execute(sql) 16 | sql = "insert into db.tb values (1650000000001, null, null, null)" 17 | cursor.execute(sql) 18 | sql = "select * from db.tb" 19 | cursor.execute(sql) 20 | 21 | for row in cursor: 22 | print(row) 23 | 24 | conn.close() 25 | -------------------------------------------------------------------------------- /taos-ws-py/examples/demo.py: -------------------------------------------------------------------------------- 1 | import taosws 2 | 3 | conn = taosws.connect("ws://localhost:6041") 4 | 5 | conn.execute("create database if not exists connwspy") 6 | conn.execute("use connwspy") 7 | conn.execute("create table if not exists stb (ts timestamp, c1 int) tags (t1 int)") 8 | conn.execute("create table if not exists tb1 using stb tags (1)") 9 | conn.execute("insert into tb1 values (now, 1)") 10 | conn.execute("insert into tb1 values (now, 2)") 11 | conn.execute("insert into tb1 values (now, 3)") 12 | 13 | result = conn.query("show databases") 14 | num_of_fields = result.field_count 15 | print(num_of_fields) 16 | 17 | for field in result.fields: 18 | print(field) 19 | for row in result: 20 | print(row) 21 | 22 | conn.execute("drop database if exists connwspy") 23 | -------------------------------------------------------------------------------- /examples/insert-lines.py: -------------------------------------------------------------------------------- 1 | import taos 2 | from taos import SmlProtocol, SmlPrecision 3 | 4 | conn = taos.connect() 5 | dbname = "pytest_line" 6 | conn.execute("drop database if exists %s" % dbname) 7 | conn.execute("create database if not exists %s precision 'us'" % dbname) 8 | conn.select_db(dbname) 9 | 10 | lines = [ 11 | 'st,t1=3i64,t2=4f64,t3="t3" c1=3i64,c3=L"pass",c2=false,c4=4f64 1626006833639000000', 12 | ] 13 | conn.schemaless_insert(lines, taos.SmlProtocol.LINE_PROTOCOL, taos.SmlPrecision.NOT_CONFIGURED) 14 | print("inserted") 15 | 16 | conn.schemaless_insert(lines, taos.SmlProtocol.LINE_PROTOCOL, taos.SmlPrecision.NOT_CONFIGURED) 17 | 18 | result = conn.query("show tables") 19 | for row in result: 20 | print(row) 21 | 22 | 23 | conn.execute("drop database if exists %s" % dbname) 24 | conn.close() 25 | -------------------------------------------------------------------------------- /examples/connect_cloud_service.py: -------------------------------------------------------------------------------- 1 | import taosrest 2 | import os 3 | 4 | 5 | def connect_to_cloud_use_token(url, token): 6 | conn = taosrest.connect(url=url, token=token) 7 | 8 | print(conn.server_info) 9 | cursor = conn.cursor() 10 | cursor.execute("drop database if exists pytest") 11 | cursor.execute("create database pytest precision 'ns' keep 365") 12 | cursor.execute("create table pytest.temperature(ts timestamp, temp int)") 13 | cursor.execute("insert into pytest.temperature values(now, 1) (now+10b, 2)") 14 | cursor.execute("select * from pytest.temperature") 15 | rows = cursor.fetchall() 16 | print(rows) 17 | 18 | 19 | if __name__ == "__main__": 20 | url = os.environ["TDENGINE_CLOUD_URL"] 21 | token = os.environ["TDENGINE_CLOUD_TOKEN"] 22 | connect_to_cloud_use_token(url, token) 23 | -------------------------------------------------------------------------------- /examples/import-json.py: -------------------------------------------------------------------------------- 1 | import json 2 | import taos 3 | import requests 4 | 5 | j = requests.get("http://api.coindesk.com/v1/bpi/currentprice.json").json() 6 | 7 | # json to sql 8 | ts = j["time"]["updatedISO"] 9 | sql = "insert into" 10 | for id in j["bpi"]: 11 | bpi = j["bpi"][id] 12 | sql += ' %s using price tags("%s","%s","%s") values("%s", %lf) ' % ( 13 | id, 14 | bpi["code"], 15 | bpi["symbol"], 16 | bpi["description"], 17 | ts, 18 | bpi["rate_float"], 19 | ) 20 | 21 | # sql to TDengine 22 | taos = taos.connect() 23 | taos.execute("create database if not exists bpi") 24 | taos.execute("use bpi") 25 | taos.execute( 26 | "create stable if not exists price (ts timestamp, rate double)" 27 | + " tags (code binary(10), symbol binary(10), description binary(100))" 28 | ) 29 | taos.execute(sql) 30 | -------------------------------------------------------------------------------- /examples/trades-to-taos.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import json 3 | import requests 4 | import taos 5 | 6 | HISTORICAL_API = "https://production.api.coindesk.com/v2/tb/price/values/" 7 | 8 | id = "BTC" 9 | end_date = datetime.datetime.utcnow() 10 | end_date_str = end_date.strftime("%FT%H:%M") 11 | 12 | start_date = end_date - datetime.timedelta(hours=24) 13 | start_date_str = start_date.strftime("%FT%H:%M") 14 | url = "https://production.api.coindesk.com/v2/tb/price/values/%s?start_date=%s&end_date=%s&ohlc=false" % ( 15 | id, 16 | start_date_str, 17 | end_date_str, 18 | ) 19 | 20 | response = requests.get(url).json() 21 | # response = json.load(open("examples/prices.json")) 22 | 23 | entries = response["data"]["entries"] 24 | 25 | taos = taos.connect(database="bpi") 26 | 27 | for entry in entries: 28 | taos.execute("insert into usd values(%d, %d)" % (entry[0], entry[1])) 29 | -------------------------------------------------------------------------------- /tests/test_info.py: -------------------------------------------------------------------------------- 1 | import taos 2 | from taos.cinterface import * 3 | 4 | from taos import * 5 | 6 | import pytest 7 | 8 | 9 | @pytest.fixture 10 | def conn(): 11 | return connect() 12 | 13 | 14 | def test_client_info(): 15 | print("client info: %s" % taos_get_client_info()) 16 | pass 17 | 18 | 19 | def test_server_info(conn): 20 | # type: (TaosConnection) -> None 21 | print("conn client info: %s" % conn.client_info) 22 | print("conn server info: %s" % conn.server_info) 23 | pass 24 | 25 | 26 | def test_log(): 27 | taos.log.setting(True, True, True, True, True, True) 28 | taos.log.info("log is info") 29 | taos.log.debug("log is debug") 30 | taos.log.debug1("log is debug") 31 | taos.log.debug2("log is debug") 32 | taos.log.debug3("log is debug") 33 | 34 | 35 | if __name__ == "__main__": 36 | test_client_info() 37 | test_server_info(connect()) 38 | -------------------------------------------------------------------------------- /dev.md: -------------------------------------------------------------------------------- 1 | # Setup development environment 2 | 3 | ## Use pipx to install isolated environments 4 | 5 | On macOS 6 | 7 | Install pipx 8 | 9 | 10 | ``` 11 | brew install pipx 12 | pipx ensurepath 13 | ``` 14 | 15 | ## Install poetry 16 | 17 | Install poetry use pipx 18 | 19 | ``` 20 | pipx install poetry 21 | ``` 22 | 23 | Install poetry with specify python version 24 | 25 | ``` 26 | pipx install --python python3.11 poetry --force 27 | ``` 28 | 29 | ## Install dependencies 30 | 31 | ``` 32 | poetry install 33 | ``` 34 | 35 | ## Run tests 36 | 37 | ``` 38 | poetry run pytest tests 39 | ``` 40 | 41 | Run specific test file 42 | 43 | ``` 44 | poetry run pytest tests/test_example.py 45 | ``` 46 | 47 | Run specific test function 48 | 49 | ``` 50 | poetry run pytest tests/test_example.py::test_function_example 51 | ``` 52 | ``` 53 | 54 | Rest api test 55 | 56 | ``` 57 | export TDENGINE_URL=localhost:6041 58 | ``` 59 | 60 | -------------------------------------------------------------------------------- /examples/connect_websocket_examples.py: -------------------------------------------------------------------------------- 1 | # ANCHOR: connect 2 | import taosws 3 | 4 | conn = taosws.connect("taosws://root:taosdata@localhost:6041") 5 | # ANCHOR_END: connect 6 | 7 | # ANCHOR: basic 8 | conn.execute("drop database if exists connwspy") 9 | conn.execute("create database if not exists connwspy") 10 | conn.execute("use connwspy") 11 | conn.execute("create table if not exists stb (ts timestamp, c1 int) tags (t1 int)") 12 | conn.execute("create table if not exists tb1 using stb tags (1)") 13 | conn.execute("insert into tb1 values (now, 1)") 14 | conn.execute("insert into tb1 values (now, 2)") 15 | conn.execute("insert into tb1 values (now, 3)") 16 | 17 | r = conn.execute("select * from stb") 18 | result = conn.query("select * from stb") 19 | num_of_fields = result.field_count 20 | print(num_of_fields) 21 | 22 | for row in result: 23 | print(row) 24 | 25 | # output: 26 | # 3 27 | # ('2023-02-28 15:56:13.329 +08:00', 1, 1) 28 | # ('2023-02-28 15:56:13.333 +08:00', 2, 1) 29 | # ('2023-02-28 15:56:13.337 +08:00', 3, 1) 30 | -------------------------------------------------------------------------------- /taos/log.py: -------------------------------------------------------------------------------- 1 | import time 2 | import os 3 | import sys 4 | 5 | 6 | # info 7 | show_info = True 8 | show_warn = True 9 | show_debug = False 10 | show_debug1 = False 11 | show_debug2 = False 12 | show_debug3 = False 13 | 14 | # set show 15 | def setting(info, warn, debug, debug1, debug2, debug3): 16 | global show_info, show_warn, show_debug, show_debug1, show_debug2, show_debug3 17 | show_info = info 18 | show_warn = warn 19 | show_debug = debug 20 | show_debug1 = debug1 21 | show_debug2 = debug2 22 | show_debug3 = debug3 23 | 24 | 25 | # info 26 | def info(msg): 27 | if show_info: 28 | print(msg) 29 | 30 | 31 | # warn 32 | def warn(msg): 33 | if show_warn: 34 | print(msg) 35 | 36 | 37 | # debug 38 | def debug(msg): 39 | if show_debug: 40 | print(msg) 41 | 42 | 43 | # debug1 44 | def debug1(msg): 45 | if show_debug1: 46 | print(msg) 47 | 48 | 49 | # debug2 50 | def debug2(msg): 51 | if show_debug2: 52 | print(msg) 53 | 54 | 55 | # debug3 56 | def debug3(msg): 57 | if show_debug3: 58 | print(msg) 59 | -------------------------------------------------------------------------------- /taos/subscription.py: -------------------------------------------------------------------------------- 1 | from taos.result import TaosResult 2 | from taos.cinterface import taos_consume, taos_unsubscribe 3 | from taos.error import OperationalError 4 | 5 | 6 | class TaosSubscription(object): 7 | """TDengine subscription object""" 8 | 9 | def __init__(self, sub, with_callback=False): 10 | self._sub = sub 11 | self._with_callback = with_callback 12 | 13 | def consume(self): 14 | """Consume rows of a subscription""" 15 | if self._sub is None: 16 | raise OperationalError("Invalid use of consume") 17 | if self._with_callback: 18 | raise OperationalError("DONOT use consume method in an subscription with callback") 19 | result = taos_consume(self._sub) 20 | return TaosResult(result) 21 | 22 | def close(self, keepProgress=True): 23 | """Close the Subscription.""" 24 | if self._sub is None: 25 | return False 26 | 27 | taos_unsubscribe(self._sub, keepProgress) 28 | self._sub = None 29 | return True 30 | 31 | def __del__(self): 32 | self.close() 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 taosdata 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/connect_websocket_with_req_id_examples.py: -------------------------------------------------------------------------------- 1 | # ANCHOR: connect 2 | import taosws 3 | 4 | conn = taosws.connect("taosws://root:taosdata@localhost:6041") 5 | # ANCHOR_END: connect 6 | 7 | # ANCHOR: basic 8 | conn.execute("drop database if exists connwspy", req_id=1) 9 | conn.execute("create database if not exists connwspy", req_id=2) 10 | conn.execute("use connwspy", req_id=3) 11 | conn.execute("create table if not exists stb (ts timestamp, c1 int) tags (t1 int)", req_id=4) 12 | conn.execute("create table if not exists tb1 using stb tags (1)", req_id=5) 13 | conn.execute("insert into tb1 values (now, 1)", req_id=6) 14 | conn.execute("insert into tb1 values (now, 2)", req_id=7) 15 | conn.execute("insert into tb1 values (now, 3)", req_id=8) 16 | 17 | r = conn.execute("select * from stb", req_id=9) 18 | result = conn.query("select * from stb", req_id=10) 19 | num_of_fields = result.field_count 20 | print(num_of_fields) 21 | 22 | for row in result: 23 | print(row) 24 | 25 | # output: 26 | # 3 27 | # ('2023-02-28 15:56:13.329 +08:00', 1, 1) 28 | # ('2023-02-28 15:56:13.333 +08:00', 2, 1) 29 | # ('2023-02-28 15:56:13.337 +08:00', 3, 1) 30 | -------------------------------------------------------------------------------- /examples/cursor_usage_native_reference.py: -------------------------------------------------------------------------------- 1 | import taos 2 | 3 | conn = taos.connect() 4 | cursor = conn.cursor() 5 | 6 | cursor.execute("DROP DATABASE IF EXISTS test") 7 | cursor.execute("CREATE DATABASE test") 8 | cursor.execute("USE test") 9 | cursor.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)") 10 | 11 | for i in range(1000): 12 | location = str(i % 10) 13 | tb = "t" + location 14 | cursor.execute(f"INSERT INTO {tb} USING weather TAGS({location}) VALUES (now+{i}a, 23.5) (now+{i + 1}a, 23.5)") 15 | 16 | cursor.execute("SELECT count(*) FROM weather") 17 | data = cursor.fetchall() 18 | print("count:", data[0][0]) 19 | cursor.execute("SELECT tbname, * FROM weather LIMIT 2") 20 | col_names = [meta[0] for meta in cursor.description] 21 | print(col_names) 22 | rows = cursor.fetchall() 23 | print(rows) 24 | 25 | cursor.close() 26 | conn.close() 27 | 28 | # output: 29 | # count: 2000 30 | # ['tbname', 'ts', 'temperature', 'location'] 31 | # row_count: -1 32 | # [('t0', datetime.datetime(2022, 4, 27, 14, 54, 24, 392000), 23.5, 0), ('t0', datetime.datetime(2022, 4, 27, 14, 54, 24, 393000), 23.5, 0)] 33 | -------------------------------------------------------------------------------- /examples/cursor_usage_native_reference_with_req_id.py: -------------------------------------------------------------------------------- 1 | import taos 2 | 3 | conn = taos.connect() 4 | cursor = conn.cursor() 5 | 6 | cursor.execute("DROP DATABASE IF EXISTS test", req_id=1) 7 | cursor.execute("CREATE DATABASE test", req_id=2) 8 | cursor.execute("USE test", req_id=3) 9 | cursor.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)", req_id=4) 10 | 11 | for i in range(1000): 12 | location = str(i % 10) 13 | tb = "t" + location 14 | cursor.execute( 15 | f"INSERT INTO {tb} USING weather TAGS({location}) VALUES (now+{i}a, 23.5) (now+{i + 1}a, 23.5)", req_id=5 + i 16 | ) 17 | 18 | cursor.execute("SELECT count(*) FROM weather", req_id=1005) 19 | data = cursor.fetchall() 20 | print("count:", data[0][0]) 21 | cursor.execute("SELECT tbname, * FROM weather LIMIT 2", req_id=1006) 22 | col_names = [meta[0] for meta in cursor.description] 23 | print(col_names) 24 | rows = cursor.fetchall() 25 | print(rows) 26 | 27 | cursor.close() 28 | conn.close() 29 | 30 | # output: 31 | # count: 2000 32 | # ['tbname', 'ts', 'temperature', 'location'] 33 | # row_count: -1 34 | # [('t0', datetime.datetime(2022, 4, 27, 14, 54, 24, 392000), 23.5, 0), ('t0', datetime.datetime(2022, 4, 27, 14, 54, 24, 393000), 23.5, 0)] 35 | -------------------------------------------------------------------------------- /taos-ws-py/tests/test_cursor_fetchmany.py: -------------------------------------------------------------------------------- 1 | import taosws 2 | import time 3 | 4 | 5 | def test_fetchmany(): 6 | try: 7 | conn = taosws.connect("taosws://root:taosdata@localhost:6041") 8 | conn.execute("drop database if exists test_173942943") 9 | conn.execute("create database test_173942943") 10 | conn.execute("use test_173942943") 11 | conn.execute("create table t0 (ts timestamp, c1 int)") 12 | 13 | num = 7000 14 | ts = time.time_ns() // 1000000 15 | for i in range(num): 16 | conn.execute(f"insert into t0 values({ts+i}, {i})") 17 | 18 | cursor = conn.cursor() 19 | cursor.execute("select * from test_173942943.t0") 20 | 21 | rows = 0 22 | while True: 23 | res = cursor.fetchmany(3345) 24 | if res is None or len(res) == 0: 25 | break 26 | else: 27 | rows += len(res) 28 | 29 | assert rows == num 30 | 31 | conn.execute("drop database test_173942943") 32 | except Exception as err: 33 | print(f"fetchmany failed, err: {err}") 34 | raise err 35 | finally: 36 | if cursor: 37 | cursor.close() 38 | if conn: 39 | conn.close() 40 | 41 | 42 | if __name__ == "__main__": 43 | test_fetchmany() 44 | -------------------------------------------------------------------------------- /examples/result_set_examples.py: -------------------------------------------------------------------------------- 1 | import taos 2 | 3 | conn = taos.connect() 4 | conn.execute("DROP DATABASE IF EXISTS test") 5 | conn.execute("CREATE DATABASE test") 6 | conn.select_db("test") 7 | conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)") 8 | # prepare data 9 | for i in range(2000): 10 | location = str(i % 10) 11 | tb = "t" + location 12 | conn.execute(f"INSERT INTO {tb} USING weather TAGS({location}) VALUES (now+{i}a, 23.5) (now+{i + 1}a, 23.5)") 13 | 14 | result: taos.TaosResult = conn.query("SELECT * FROM weather") 15 | 16 | block_index = 0 17 | blocks: taos.TaosBlocks = result.blocks_iter() 18 | for rows, length in blocks: 19 | print("block ", block_index, " length", length) 20 | print("first row in this block:", rows[0]) 21 | block_index += 1 22 | 23 | conn.close() 24 | 25 | # possible output: 26 | # block 0 length 1200 27 | # first row in this block: (datetime.datetime(2022, 4, 27, 15, 14, 52, 46000), 23.5, 0) 28 | # block 1 length 1200 29 | # first row in this block: (datetime.datetime(2022, 4, 27, 15, 14, 52, 76000), 23.5, 3) 30 | # block 2 length 1200 31 | # first row in this block: (datetime.datetime(2022, 4, 27, 15, 14, 52, 99000), 23.5, 6) 32 | # block 3 length 400 33 | # first row in this block: (datetime.datetime(2022, 4, 27, 15, 14, 52, 122000), 23.5, 9) 34 | -------------------------------------------------------------------------------- /taos-ws-py/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "taos-ws-py" 3 | version = "0.6.3" 4 | edition = "2021" 5 | publish = false 6 | 7 | [lib] 8 | name = "taosws" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | anyhow = "1" 13 | chrono = "0.4" 14 | chrono-tz = "0.10.4" 15 | iana-time-zone = "0.1.63" 16 | log = "0.4" 17 | pretty_env_logger = "0.5" 18 | rustls = "=0.21.7" 19 | rustls-webpki = "=0.101.6" 20 | sct = "=0.7.0" 21 | serde_json = "1" 22 | shadow-rs = { version = "0.33.0", default-features = false } 23 | 24 | [build-dependencies] 25 | shadow-rs = { version = "0.33.0", default-features = false } 26 | 27 | [dependencies.pyo3] 28 | version = "0.17.3" 29 | features = ["extension-module", "anyhow", "chrono", "abi3-py37"] 30 | 31 | [target.'cfg(windows)'.dependencies] 32 | taos = { git = "https://github.com/taosdata/taos-connector-rust.git", branch = "main", default-features = false, features = [ 33 | "optin", 34 | "ws-rustls", 35 | "ws-rustls-aws-lc-crypto-provider", 36 | ] } 37 | [target.'cfg(unix)'.dependencies] 38 | taos = { git = "https://github.com/taosdata/taos-connector-rust.git", branch = "main", default-features = false, features = [ 39 | "optin", 40 | "ws-rustls", 41 | "ws-rustls-ring-crypto-provider", 42 | ] } 43 | 44 | [patch.crates-io] 45 | ring = { git = "https://github.com/taosdata/ring.git", branch = "fix/loongarch" } 46 | -------------------------------------------------------------------------------- /examples/result_set_with_req_id_examples.py: -------------------------------------------------------------------------------- 1 | import taos 2 | 3 | conn = taos.connect() 4 | conn.execute("DROP DATABASE IF EXISTS test", req_id=1) 5 | conn.execute("CREATE DATABASE test", req_id=2) 6 | conn.select_db("test") 7 | conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)", req_id=3) 8 | # prepare data 9 | for i in range(2000): 10 | location = str(i % 10) 11 | tb = "t" + location 12 | conn.execute( 13 | f"INSERT INTO {tb} USING weather TAGS({location}) VALUES (now+{i}a, 23.5) (now+{i + 1}a, 23.5)", req_id=4 + i 14 | ) 15 | 16 | result: taos.TaosResult = conn.query("SELECT * FROM weather", req_id=2004) 17 | 18 | block_index = 0 19 | blocks: taos.TaosBlocks = result.blocks_iter() 20 | for rows, length in blocks: 21 | print("block ", block_index, " length", length) 22 | print("first row in this block:", rows[0]) 23 | block_index += 1 24 | 25 | conn.close() 26 | 27 | # possible output: 28 | # block 0 length 1200 29 | # first row in this block: (datetime.datetime(2022, 4, 27, 15, 14, 52, 46000), 23.5, 0) 30 | # block 1 length 1200 31 | # first row in this block: (datetime.datetime(2022, 4, 27, 15, 14, 52, 76000), 23.5, 3) 32 | # block 2 length 1200 33 | # first row in this block: (datetime.datetime(2022, 4, 27, 15, 14, 52, 99000), 23.5, 6) 34 | # block 3 length 400 35 | # first row in this block: (datetime.datetime(2022, 4, 27, 15, 14, 52, 122000), 23.5, 9) 36 | -------------------------------------------------------------------------------- /ci/changelog-generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | path=$1 4 | prefix=$2 5 | last= 6 | if [ "$prefix" = "" ]; then 7 | last=$(git describe --tags --abbrev=0 2>/dev/null) 8 | else 9 | last=$(git tag --sort=-committerdate |grep $prefix |head -n1 2>/dev/null) 10 | fi 11 | 12 | if [ "$last" = "" ]; then 13 | git log --pretty=format:'%s' $path | sort -k2n | uniq > ./releaseNotes.tmp 14 | else 15 | git log --pretty=format:'%s' $last..HEAD $path | sort -k2n | uniq > ./releaseNotes.tmp 16 | fi 17 | 18 | function part() { 19 | name=$1 20 | pattern=$2 21 | changes=$(egrep -E '\[\w+-\d+\]\s*<('$pattern')>:' ./releaseNotes.tmp | sed -E 's/ *<('$pattern')>//' |sed 's/[ci skip]\s*//' | awk -F: '{print "- " $1 ":" $2}'| sed -E 's/^\s+|\s$//' | sort|uniq) 22 | changes2=$(egrep -E '^('$pattern')(\(.*\))?:' ./releaseNotes.tmp | sed -E 's/^('$pattern')(\(.*\)):\s*/**\2**: /' | sed -E 's/^('$pattern'):\s*//'|sed -E 's/\[ci skip\]\s*//' | awk '{print "- " $0}' | sed -E 's/^\s+|\s$//' |sort|uniq) 23 | 24 | if [ "$changes" != "" ] || [ "$changes2" != "" ]; then 25 | echo "### $name:" 26 | echo "" 27 | [ "$changes" != "" ] && echo "$changes" 28 | [ "$changes2" != "" ] && echo "$changes2" 29 | echo "" 30 | fi 31 | } 32 | 33 | part "Features" "feature|feat|impr" 34 | part "Bug Fixes" "bugfix|fix" 35 | part "Enhancements" "enhance|enh" 36 | part "Tests" "test" 37 | part "Documents" "docs|doc" 38 | 39 | rm -f ./releaseNotes.tmp 40 | -------------------------------------------------------------------------------- /tests/test_vgroup_id.py: -------------------------------------------------------------------------------- 1 | #! encoding=utf-8 2 | import taos 3 | from utils import tear_down_database 4 | 5 | 6 | def test_get_table_vgroup_id(): 7 | conn = taos.connect() 8 | res = conn.query("select server_version()") 9 | data = res.fetch_all() 10 | res.close() 11 | if data[0][0] < "3.0.2.0": 12 | return 13 | 14 | conn.execute("drop database if exists test_get_table_vgroup_id") 15 | conn.execute("create database if not exists test_get_table_vgroup_id") 16 | conn.execute( 17 | "create stable test_get_table_vgroup_id.meters (ts timestamp, current float, voltage int, phase float)" 18 | " tags (location binary(64), groupId int)" 19 | ) 20 | conn.execute( 21 | "create table test_get_table_vgroup_id.d0 using test_get_table_vgroup_id.meters " 22 | "tags ('California.SanFrancisco', 1)" 23 | ) 24 | conn.execute( 25 | "create table test_get_table_vgroup_id.d1 using test_get_table_vgroup_id.meters " 26 | "tags ('California.LosAngles', 2)" 27 | ) 28 | 29 | vg_id = conn.get_table_vgroup_id("test_get_table_vgroup_id", "d0") 30 | print(vg_id) 31 | print("\n") 32 | vg_id = conn.get_table_vgroup_id("test_get_table_vgroup_id", "d1") 33 | print(vg_id) 34 | print("\n") 35 | db_name = "test_get_table_vgroup_id" 36 | tear_down_database(conn, db_name) 37 | conn.close() 38 | 39 | 40 | if __name__ == "__main__": 41 | test_get_table_vgroup_id() 42 | -------------------------------------------------------------------------------- /taos-ws-py/src/field.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | use ::taos::Field; 4 | 5 | #[pyclass] 6 | pub(crate) struct TaosField { 7 | _inner: Field, 8 | } 9 | 10 | impl From for TaosField { 11 | fn from(value: Field) -> Self { 12 | Self { _inner: value } 13 | } 14 | } 15 | 16 | impl From<&Field> for TaosField { 17 | fn from(value: &Field) -> Self { 18 | Self { 19 | _inner: value.clone(), 20 | } 21 | } 22 | } 23 | 24 | impl TaosField { 25 | #[allow(dead_code)] 26 | fn new(inner: &Field) -> Self { 27 | Self { 28 | _inner: inner.clone(), 29 | } 30 | } 31 | } 32 | 33 | #[pymethods] 34 | impl TaosField { 35 | /// Field name. 36 | fn name(&self) -> &str { 37 | self._inner.name() 38 | } 39 | 40 | /// Field type name 41 | fn r#type(&self) -> &str { 42 | self._inner.ty().name() 43 | } 44 | 45 | /// Declaration max-bytes in field. 46 | fn bytes(&self) -> u32 { 47 | self._inner.bytes() 48 | } 49 | 50 | fn __repr__(&self) -> PyResult { 51 | Ok(format!( 52 | "{{name: {}, type: {}, bytes: {}}}", 53 | self.name(), 54 | self.r#type(), 55 | self.bytes() 56 | )) 57 | } 58 | 59 | fn __str__(&self) -> PyResult { 60 | Ok(format!( 61 | "{{name: {}, type: {}, bytes: {}}}", 62 | self.name(), 63 | self.r#type(), 64 | self.bytes() 65 | )) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /taosrest/errors.py: -------------------------------------------------------------------------------- 1 | class Error(Exception): 2 | def __init__(self, msg=None, errno=0xFFFF, status=""): 3 | self.msg = msg 4 | self.errno = errno 5 | self.status = status 6 | if self.status != "": 7 | self._full_msg = "[%s]: %s" % (self.status, self.msg) 8 | else: 9 | self._full_msg = "[0x%04x]: %s" % (self.errno & 0xFFFF, self.msg) 10 | 11 | def __str__(self): 12 | return self._full_msg 13 | 14 | 15 | class ExecutionError(Error): 16 | """Run sql error""" 17 | 18 | pass 19 | 20 | 21 | class HTTPError(Exception): 22 | def __init__(self, status_code, msg=None): 23 | self.msg = msg 24 | self.status_code = status_code 25 | self._full_msg = f"HTTP response code {status_code}" 26 | if msg: 27 | self._full_msg += "." + msg 28 | 29 | def __str__(self): 30 | return self._full_msg 31 | 32 | 33 | class ConnectError(Error): 34 | """Exception raised for connection failed""" 35 | 36 | pass 37 | 38 | 39 | class InterfaceError(Error): 40 | pass 41 | 42 | 43 | class DatabaseError(Error): 44 | pass 45 | 46 | 47 | class InternalError(DatabaseError): 48 | pass 49 | 50 | 51 | class OperationalError(DatabaseError): 52 | pass 53 | 54 | 55 | class ProgrammingError(DatabaseError): 56 | pass 57 | 58 | 59 | class IntegrityError(DatabaseError): 60 | pass 61 | 62 | 63 | class DataError(DatabaseError): 64 | pass 65 | 66 | 67 | class NotSupportedError(DatabaseError): 68 | pass 69 | -------------------------------------------------------------------------------- /examples/v2/subscribe-async.py: -------------------------------------------------------------------------------- 1 | from taos import * 2 | from ctypes import * 3 | 4 | import time 5 | 6 | 7 | def subscribe_callback(p_sub, p_result, p_param, errno): 8 | # type: (c_void_p, c_void_p, c_void_p, c_int) -> None 9 | print("# fetch in callback") 10 | result = TaosResult(c_void_p(p_result)) 11 | result.check_error(errno) 12 | for row in result.rows_iter(): 13 | ts, n = row() 14 | print(ts, n) 15 | 16 | 17 | def test_subscribe_callback(conn): 18 | # type: (TaosConnection) -> None 19 | dbname = "pytest_taos_subscribe_callback" 20 | try: 21 | print("drop if exists") 22 | conn.execute("drop database if exists %s" % dbname) 23 | print("create database") 24 | conn.execute("create database if not exists %s" % dbname) 25 | print("create table") 26 | # conn.execute("use %s" % dbname) 27 | conn.execute("create table if not exists %s.log(ts timestamp, n int)" % dbname) 28 | 29 | print("# subscribe with callback") 30 | sub = conn.subscribe(False, "test", "select * from %s.log" % dbname, 1000, subscribe_callback) 31 | 32 | for i in range(10): 33 | conn.execute("insert into %s.log values(now, %d)" % (dbname, i)) 34 | time.sleep(0.7) 35 | sub.close() 36 | 37 | conn.execute("drop database if exists %s" % dbname) 38 | # conn.close() 39 | except Exception as err: 40 | conn.execute("drop database if exists %s" % dbname) 41 | # conn.close() 42 | raise err 43 | 44 | 45 | if __name__ == "__main__": 46 | test_subscribe_callback(connect()) 47 | -------------------------------------------------------------------------------- /taos-ws-py/dev.md: -------------------------------------------------------------------------------- 1 | # taos-ws-py 2 | 3 | This is a Python websocket client for TDengine. 4 | 5 | ## Init 6 | 7 | ### mac 8 | 9 | install rust 10 | 11 | ```bash 12 | 13 | curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh 14 | 15 | source "$HOME/.cargo/env" 16 | 17 | ``` 18 | 19 | ```bash 20 | 21 | pip3 install maturin 22 | 23 | ``` 24 | 25 | ### linux 26 | 27 | install rust 28 | 29 | ```bash 30 | 31 | curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh 32 | 33 | source "$HOME/.cargo/env" 34 | 35 | ``` 36 | 37 | ```bash 38 | 39 | pip3 install maturin 40 | 41 | ``` 42 | 43 | ## Build and Installation 44 | 45 | ### mac 46 | 47 | ```bash 48 | 49 | maturin build --strip && pip3 install ./target/wheels/taos_ws_py-0.3.2-cp37-abi3-macosx_10_7_x86_64.whl --force-reinstall 50 | 51 | ``` 52 | 53 | ### linux 54 | 55 | ```bash 56 | 57 | python3 -m maturin build --strip && pip3 install ./target/wheels/taos_ws_py-0.3.2-cp37-abi3-manylinux_2_31_x86_64.whl --force-reinstall 58 | 59 | ``` 60 | 61 | ## Test 62 | 63 | ### mac 64 | 65 | ```bash 66 | 67 | pip3 install pytest 68 | # testcase depends on those packages 69 | pip3 install pandas sqlalchemy python-dotenv 70 | 71 | export TDENGINE_URL=localhost:6041 72 | 73 | pytest tests 74 | 75 | ``` 76 | 77 | ### linux 78 | 79 | ```bash 80 | 81 | pip3 install pytest 82 | # testcase depends on those packages 83 | pip3 install pandas sqlalchemy python-dotenv 84 | 85 | export TDENGINE_URL=localhost:6041 86 | 87 | python3 -m pytest tests 88 | 89 | ``` 90 | 91 | ## Release 92 | 93 | ```bash 94 | 95 | maturin build --release --future-incompat-report --strip 96 | 97 | ``` -------------------------------------------------------------------------------- /taosrest/sqlalchemy.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from sqlalchemy import types as sqltypes 3 | from sqlalchemy.engine import default, reflection 4 | from sqlalchemy import text 5 | 6 | # taos.sqlalchemy.BaseDialect 7 | import taos 8 | 9 | # 10 | # ---------------- taos rest impl ------------- 11 | # 12 | import taos.sqlalchemy 13 | import taosrest 14 | 15 | # rest connect 16 | class AlchemyRestConnection: 17 | threadsafety = 0 18 | paramstyle = "pyformat" 19 | Error = taosrest.Error 20 | # connect 21 | def connect(self, **kwargs): 22 | host = kwargs["host"] if "host" in kwargs else None 23 | port = kwargs["port"] if "port" in kwargs else None 24 | user = kwargs["username"] if "username" in kwargs else "root" 25 | password = kwargs["password"] if "password" in kwargs else "taosdata" 26 | database = kwargs["database"] if "database" in kwargs else None 27 | token = kwargs["token"] if "token" in kwargs else None 28 | 29 | if not host: 30 | host = "localhost" 31 | if host == "localhost" and not port: 32 | port = 6041 33 | 34 | url = f"http://{host}" 35 | if port: 36 | url += f":{port}" 37 | return taosrest.connect(url=url, user=user, password=password, database=database, token=token) 38 | 39 | 40 | # rest dialect 41 | class TaosRestDialect(taos.sqlalchemy.BaseDialect): 42 | name = "taosrest" 43 | driver = "taosrest" 44 | 45 | @classmethod 46 | def dbapi(cls): 47 | return AlchemyRestConnection() 48 | 49 | @classmethod 50 | def import_dbapi(cls): 51 | return AlchemyRestConnection() 52 | -------------------------------------------------------------------------------- /tests/test_native_cursor.py: -------------------------------------------------------------------------------- 1 | import taos 2 | from utils import tear_down_database 3 | 4 | env = { 5 | "user": "root", 6 | "password": "taosdata", 7 | "host": "localhost", 8 | "port": 6030, 9 | } 10 | 11 | 12 | def make_context(config): 13 | db_protocol = config.get("db_protocol", "taos") 14 | db_user = config["user"] 15 | db_pass = config["password"] 16 | db_host = config["host"] 17 | db_port = config["port"] 18 | 19 | db_url = f"{db_protocol}://{db_user}:{db_pass}@{db_host}:{db_port}" 20 | print("dsn: ", db_url) 21 | 22 | conn = taos.connect(**config) 23 | 24 | db_name = config.get("database", "c_cursor") 25 | 26 | return conn, db_name 27 | 28 | 29 | def test_cursor(): 30 | conn, db_name = make_context(env) 31 | 32 | cursor = conn.cursor() 33 | 34 | cursor.execute(f"DROP DATABASE IF EXISTS {db_name}") 35 | cursor.execute(f"CREATE DATABASE {db_name}") 36 | cursor.execute(f"USE {db_name}") 37 | 38 | cursor.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)") 39 | 40 | cursor.execute(f"INSERT INTO t1 USING weather TAGS(1) VALUES (now, 23.5) (now+100a, 23.5)") 41 | 42 | assert cursor.rowcount == 2 43 | 44 | cursor.execute("SELECT tbname, ts, temperature, location FROM weather") 45 | # rowcount can only get correct value after fetching all data 46 | all_data = cursor.fetchall() 47 | print(f"{'*' * 20} row count: {cursor.rowcount} {'*' * 20}") 48 | for row in all_data: 49 | print(row) 50 | assert cursor.rowcount == 2 51 | tear_down_database(cursor, db_name) 52 | cursor.close() 53 | conn.close() 54 | -------------------------------------------------------------------------------- /examples/bind-row.py: -------------------------------------------------------------------------------- 1 | from taos import * 2 | 3 | conn = connect() 4 | 5 | dbname = "pytest_taos_stmt" 6 | conn.execute("drop database if exists %s" % dbname) 7 | conn.execute("create database if not exists %s" % dbname) 8 | conn.select_db(dbname) 9 | 10 | conn.execute( 11 | "create table if not exists log(ts timestamp, bo bool, nil tinyint, \ 12 | ti tinyint, si smallint, ii int, bi bigint, tu tinyint unsigned, \ 13 | su smallint unsigned, iu int unsigned, bu bigint unsigned, \ 14 | ff float, dd double, bb binary(100), nn nchar(100), tt timestamp)", 15 | ) 16 | 17 | stmt = conn.statement("insert into log values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)") 18 | 19 | params = new_bind_params(16) 20 | params[0].timestamp(1626861392589) 21 | params[1].bool(True) 22 | params[2].tinyint(None) 23 | params[3].tinyint(2) 24 | params[4].smallint(3) 25 | params[5].int(4) 26 | params[6].bigint(5) 27 | params[7].tinyint_unsigned(6) 28 | params[8].smallint_unsigned(7) 29 | params[9].int_unsigned(8) 30 | params[10].bigint_unsigned(9) 31 | params[11].float(10.1) 32 | params[12].double(10.11) 33 | params[13].binary("hello") 34 | params[14].nchar("stmt") 35 | params[15].timestamp(1626861392589) 36 | stmt.bind_param(params) 37 | 38 | params[0].timestamp(1626861392590) 39 | params[15].timestamp(None) 40 | stmt.bind_param(params) 41 | stmt.execute() 42 | 43 | assert stmt.affected_rows == 2 44 | # No need to explicitly close, but ok for you 45 | # result.close() 46 | 47 | result = conn.query("select * from log") 48 | 49 | for row in result: 50 | print(row) 51 | 52 | # No need to explicitly close, but ok for you 53 | result.close() 54 | stmt.close() 55 | conn.close() 56 | -------------------------------------------------------------------------------- /examples/bind-multi.py: -------------------------------------------------------------------------------- 1 | # encoding:UTF-8 2 | from taos import * 3 | 4 | conn = connect() 5 | 6 | dbname = "pytest_taos_stmt_multi" 7 | conn.execute("drop database if exists %s" % dbname) 8 | conn.execute("create database if not exists %s" % dbname) 9 | conn.select_db(dbname) 10 | 11 | conn.execute( 12 | "create table if not exists log(ts timestamp, bo bool, nil tinyint, \ 13 | ti tinyint, si smallint, ii int, bi bigint, tu tinyint unsigned, \ 14 | su smallint unsigned, iu int unsigned, bu bigint unsigned, \ 15 | ff float, dd double, bb binary(100), nn nchar(100), tt timestamp)", 16 | ) 17 | 18 | stmt = conn.statement("insert into log values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)") 19 | 20 | params = new_multi_binds(16) 21 | params[0].timestamp((1626861392589, 1626861392590, 1626861392591)) 22 | params[1].bool((True, None, False)) 23 | params[2].tinyint([-128, -128, None]) # -128 is tinyint null 24 | params[3].tinyint([0, 127, None]) 25 | params[4].smallint([3, None, 2]) 26 | params[5].int([3, 4, None]) 27 | params[6].bigint([3, 4, None]) 28 | params[7].tinyint_unsigned([3, 4, None]) 29 | params[8].smallint_unsigned([3, 4, None]) 30 | params[9].int_unsigned([3, 4, None]) 31 | params[10].bigint_unsigned([3, 4, None]) 32 | params[11].float([3, None, 1]) 33 | params[12].double([3, None, 1.2]) 34 | params[13].binary(["abc", "dddafadfadfadfadfa", None]) 35 | params[14].nchar(["涛思数据", None, "a long string with 中文字符"]) 36 | params[15].timestamp([None, None, 1626861392591]) 37 | stmt.bind_param_batch(params) 38 | stmt.execute() 39 | 40 | assert stmt.affected_rows == 3 41 | 42 | result = conn.query("select * from log") 43 | for row in result: 44 | print(row) 45 | 46 | conn.close() 47 | -------------------------------------------------------------------------------- /examples/v2/subscribe-sync.py: -------------------------------------------------------------------------------- 1 | import taos 2 | import random 3 | 4 | conn = taos.connect() 5 | dbname = "pytest_taos_subscribe" 6 | conn.execute("drop database if exists %s" % dbname) 7 | conn.execute("create database if not exists %s" % dbname) 8 | conn.select_db(dbname) 9 | conn.execute("create table if not exists log(ts timestamp, n int)") 10 | for i in range(10): 11 | conn.execute("insert into log values(now, %d)" % i) 12 | 13 | sub = conn.subscribe(False, "test", "select * from log", 1000) 14 | print("# consume from begin") 15 | for ts, n in sub.consume(): 16 | print(ts, n) 17 | 18 | print("# consume new data") 19 | for i in range(5): 20 | conn.execute("insert into log values(now, %d)(now+1s, %d)" % (i, i)) 21 | result = sub.consume() 22 | for ts, n in result: 23 | print(ts, n) 24 | 25 | sub.close(True) 26 | print("# keep progress consume") 27 | sub = conn.subscribe(False, "test", "select * from log", 1000) 28 | result = sub.consume() 29 | rows = result.fetch_all() 30 | # consume from latest subscription needs root privilege(for /var/lib/taos). 31 | assert result.row_count == 0 32 | print("## consumed ", len(rows), "rows") 33 | 34 | print("# consume with a stop condition") 35 | for i in range(10): 36 | conn.execute("insert into log values(now, %d)" % random.randint(0, 10)) 37 | result = sub.consume() 38 | try: 39 | ts, n = next(result) 40 | print(ts, n) 41 | if n > 5: 42 | result.stop_query() 43 | print("## stopped") 44 | break 45 | except StopIteration: 46 | continue 47 | 48 | sub.close() 49 | 50 | # sub.close() 51 | 52 | conn.execute("drop database if exists %s" % dbname) 53 | # conn.close() 54 | -------------------------------------------------------------------------------- /examples/tmq_consumer.py: -------------------------------------------------------------------------------- 1 | from taos.tmq import Consumer 2 | import taos 3 | 4 | 5 | def init_tmq_env(db, topic): 6 | conn = taos.connect() 7 | conn.execute("drop topic if exists {}".format(topic)) 8 | conn.execute("drop database if exists {}".format(db)) 9 | conn.execute("create database if not exists {} WAL_RETENTION_PERIOD 3600000 ".format(db)) 10 | conn.select_db(db) 11 | conn.execute("create stable if not exists stb1 (ts timestamp, c1 int, c2 float, c3 binary(10)) tags(t1 int)") 12 | conn.execute("create table if not exists tb1 using stb1 tags(1)") 13 | conn.execute("create table if not exists tb2 using stb1 tags(2)") 14 | conn.execute("create table if not exists tb3 using stb1 tags(3)") 15 | conn.execute("create topic if not exists {} as select ts, c1, c2, c3 from stb1".format(topic)) 16 | conn.execute("insert into tb1 values (now-2s, 1, 1.0, 'tmq test')") 17 | conn.execute("insert into tb2 values (now-1s, 2, 2.0, 'tmq test')") 18 | conn.execute("insert into tb3 values (now, 3, 3.0, 'tmq test')") 19 | 20 | 21 | if __name__ == "__main__": 22 | # init env 23 | init_tmq_env("tmq_test", "tmq_test_topic") 24 | consumer = Consumer( 25 | { 26 | "group.id": "3", 27 | "td.connect.user": "root", 28 | "td.connect.pass": "taosdata", 29 | "msg.with.table.name": "true", 30 | "enable.auto.commit": "true", 31 | } 32 | ) 33 | consumer.subscribe(["tmq_test_topic"]) 34 | 35 | while True: 36 | res = consumer.poll(100) 37 | if not res: 38 | continue 39 | err = res.error() 40 | if err is not None: 41 | raise err 42 | val = res.value() 43 | 44 | for block in val: 45 | print(block.fetchall()) 46 | -------------------------------------------------------------------------------- /taos-ws-py/tests/test_conn.py: -------------------------------------------------------------------------------- 1 | import taosws 2 | 3 | 4 | def test_ws_connect(): 5 | print("-" * 40) 6 | print("test_ws_connect") 7 | conn = taosws.connect("taosws://root:taosdata@localhost:6041") 8 | r = conn.query_with_req_id("show dnodes", 1) 9 | print("r: ", r.fields) 10 | print("test_ws_connect done") 11 | print("-" * 40) 12 | 13 | 14 | def test_default_connect(): 15 | print("-" * 40) 16 | print("test_default_connect") 17 | conn = taosws.connect() 18 | r = conn.query_with_req_id("show dnodes", 1) 19 | print("r: ", r.fields) 20 | print("test_default_connect done") 21 | print("-" * 40) 22 | 23 | 24 | def test_connect_invalid_user(): 25 | print("-" * 40) 26 | print("test_connect_invalid_user") 27 | try: 28 | conn = taosws.connect( 29 | user="taos", 30 | password="taosdata", 31 | host="localhost", 32 | port=6041, 33 | ) 34 | r = conn.query_with_req_id("show dnodes", 1) 35 | print("r: ", r.fields) 36 | except Exception as e: 37 | print("except invalid_user: ", e) 38 | print("test_connect_invalid_user done") 39 | print("-" * 40) 40 | 41 | 42 | def show_env(): 43 | import os 44 | 45 | print("-" * 40) 46 | print("TAOS_LIBRARY_PATH: ", os.environ.get("TAOS_LIBRARY_PATH")) 47 | print("TAOS_CONFIG_DIR: ", os.environ.get("TAOS_CONFIG_DIR")) 48 | print("TAOS_C_CLIENT_VERSION: ", os.environ.get("TAOS_C_CLIENT_VERSION")) 49 | print("TAOS_C_CLIENT_VERSION_STR: ", os.environ.get("TAOS_C_CLIENT_VERSION_STR")) 50 | print("taosws.__version__", taosws.__version__) 51 | print("-" * 40) 52 | 53 | 54 | if __name__ == "__main__": 55 | show_env() 56 | test_ws_connect() 57 | test_default_connect() 58 | test_connect_invalid_user() 59 | -------------------------------------------------------------------------------- /tests/test_cursor_logfile.py: -------------------------------------------------------------------------------- 1 | from os import unlink 2 | from utils import tear_down_database 3 | import taos 4 | 5 | 6 | def test_logfile(): 7 | conn = taos.connect() 8 | cursor = conn.cursor() 9 | 10 | try: 11 | unlink("log.txt") 12 | except Exception: 13 | pass 14 | cursor.log("log.txt") 15 | cursor.execute("DROP DATABASE IF EXISTS test") 16 | cursor.execute("CREATE DATABASE test") 17 | cursor.execute("USE test") 18 | cursor.execute( 19 | "CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT, city NCHAR(100), country BINARY(100), town VARBINARY(100)) TAGS (location INT)" 20 | ) 21 | cursor.execute( 22 | f"INSERT INTO t1 USING weather TAGS(1) VALUES (now, 23.5, 'tianjin', 'china', 'wuqing') (now+100a, 23.5, 'tianjin', 'china', 'wuqing')" 23 | ) 24 | assert cursor.rowcount == 2 25 | cursor.execute("SELECT tbname, ts, temperature, city, country, town, location FROM weather LIMIT 1") 26 | # rowcount can only get correct value after fetching all data 27 | all_data = cursor.fetchall() 28 | assert cursor.rowcount == 1 29 | db_name = "test" 30 | tear_down_database(cursor, db_name) 31 | cursor.close() 32 | conn.close() 33 | 34 | logs = open("log.txt", encoding="utf-8") 35 | txt = logs.read().splitlines() 36 | assert txt == [ 37 | "DROP DATABASE IF EXISTS test;", 38 | "CREATE DATABASE test;", 39 | "USE test;", 40 | "CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT, city NCHAR(100), country BINARY(100), town VARBINARY(100)) TAGS (location INT);", 41 | "INSERT INTO t1 USING weather TAGS(1) VALUES (now, 23.5, 'tianjin', 'china', 'wuqing') (now+100a, 23.5, 'tianjin', 'china', 'wuqing');", 42 | "SELECT tbname, ts, temperature, city, country, town, location FROM weather LIMIT 1;", 43 | "DROP DATABASE IF EXISTS test;", 44 | ] 45 | unlink("log.txt") 46 | -------------------------------------------------------------------------------- /examples/connection_usage_native_reference.py: -------------------------------------------------------------------------------- 1 | import taos 2 | 3 | # ANCHOR: insert 4 | conn = taos.connect() 5 | # Execute a sql, ignore the result set, just get affected rows. It's useful for DDL and DML statement. 6 | conn.execute("DROP DATABASE IF EXISTS test") 7 | conn.execute("CREATE DATABASE test") 8 | # change database. same as execute "USE db" 9 | conn.select_db("test") 10 | conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)") 11 | affected_row = conn.execute("INSERT INTO t1 USING weather TAGS(1) VALUES (now, 23.5) (now+1m, 23.5) (now+2m, 24.4)") 12 | print("affected_row", affected_row) 13 | # output: 14 | # affected_row 3 15 | # ANCHOR_END: insert 16 | 17 | # ANCHOR: query 18 | # Execute a sql and get its result set. It's useful for SELECT statement 19 | result = conn.query("SELECT * from weather") 20 | 21 | # Get fields from result 22 | fields = result.fields 23 | for field in fields: 24 | print(field) # {name: ts, type: 9, bytes: 8} 25 | 26 | # output: 27 | # {name: ts, type: 9, bytes: 8} 28 | # {name: temperature, type: 6, bytes: 4} 29 | # {name: location, type: 4, bytes: 4} 30 | 31 | # Get data from result as list of tuple 32 | data = result.fetch_all() 33 | print(data) 34 | # output: 35 | # [(datetime.datetime(2022, 4, 27, 9, 4, 25, 367000), 23.5, 1), (datetime.datetime(2022, 4, 27, 9, 5, 25, 367000), 23.5, 1), (datetime.datetime(2022, 4, 27, 9, 6, 25, 367000), 24.399999618530273, 1)] 36 | 37 | # Or get data from result as a list of dict 38 | # map_data = result.fetch_all_into_dict() 39 | # print(map_data) 40 | # output: 41 | # [{'ts': datetime.datetime(2022, 4, 27, 9, 1, 15, 343000), 'temperature': 23.5, 'location': 1}, {'ts': datetime.datetime(2022, 4, 27, 9, 2, 15, 343000), 'temperature': 23.5, 'location': 1}, {'ts': datetime.datetime(2022, 4, 27, 9, 3, 15, 343000), 'temperature': 24.399999618530273, 'location': 1}] 42 | # ANCHOR_END: query 43 | 44 | 45 | conn.close() 46 | -------------------------------------------------------------------------------- /tests/test_rest_api.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | from urllib.request import urlopen, Request 4 | 5 | import pytest 6 | from dotenv import load_dotenv 7 | 8 | from decorators import check_env 9 | 10 | load_dotenv() 11 | 12 | default_token = "/KfeAzX/f9na8qdtNZmtONryp201ma04bEl8LcvLUd7a8qdtNZmtONryp201ma04" 13 | 14 | 15 | @check_env 16 | def test_login(): 17 | url = os.environ["TDENGINE_URL"] 18 | response = urlopen(f"http://{url}/rest/login/root/taosdata") 19 | resp = json.load(response) 20 | print(resp) 21 | assert "code" in resp and resp["code"] == 0 22 | assert "desc" in resp and resp["desc"] == default_token 23 | 24 | 25 | @check_env 26 | def test_wrong_password(): 27 | url = os.environ["TDENGINE_URL"] 28 | try: 29 | urlopen(f"http://{url}/rest/login/root/taosdata") 30 | except Exception as e: 31 | print(f"error: <{e}>") 32 | assert "HTTP Error 401: Unauthorized" in str(e), "wrong password should return Unauthorized" 33 | return 34 | 35 | 36 | @check_env 37 | def test_server_version(): 38 | url = "http://localhost:6041/rest/sql" 39 | data = "select server_version()".encode("ascii") 40 | 41 | headers = {"Authorization": "Taosd " + default_token} 42 | request = Request(url, data, headers) 43 | response = urlopen(request) 44 | resp = json.load(response) 45 | print(resp) 46 | assert resp["rows"] == 1 47 | assert len(resp["column_meta"]) == 1 48 | assert len(resp["data"]) == 1 49 | 50 | 51 | @check_env 52 | def test_wrong_sql(): 53 | """ 54 | {'code': 9730, 'desc': 'Table does not exist: notable'} 55 | """ 56 | url = "http://localhost:6041/rest/sql" 57 | data = "select * from nodb.notable".encode("utf8") 58 | 59 | headers = {"Authorization": "Taosd " + default_token} 60 | request = Request(url, data, headers) 61 | response = urlopen(request) 62 | resp = json.load(response) 63 | print("\n", resp) 64 | assert "code" in resp and resp["code"] != 0 65 | -------------------------------------------------------------------------------- /examples/connection_usage_native_reference_with_req_id.py: -------------------------------------------------------------------------------- 1 | import taos 2 | 3 | # ANCHOR: insert 4 | conn = taos.connect() 5 | # Execute a sql, ignore the result set, just get affected rows. It's useful for DDL and DML statement. 6 | conn.execute("DROP DATABASE IF EXISTS test", req_id=1) 7 | conn.execute("CREATE DATABASE test", req_id=2) 8 | # change database. same as execute "USE db" 9 | conn.select_db("test") 10 | conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)", req_id=3) 11 | affected_row = conn.execute( 12 | "INSERT INTO t1 USING weather TAGS(1) VALUES (now, 23.5) (now+1m, 23.5) (now+2m, 24.4)", req_id=4 13 | ) 14 | print("affected_row", affected_row) 15 | # output: 16 | # affected_row 3 17 | # ANCHOR_END: insert 18 | 19 | # ANCHOR: query 20 | # Execute a sql and get its result set. It's useful for SELECT statement 21 | result = conn.query("SELECT * from weather", req_id=5) 22 | 23 | # Get fields from result 24 | fields = result.fields 25 | for field in fields: 26 | print(field) # {name: ts, type: 9, bytes: 8} 27 | 28 | # output: 29 | # {name: ts, type: 9, bytes: 8} 30 | # {name: temperature, type: 6, bytes: 4} 31 | # {name: location, type: 4, bytes: 4} 32 | 33 | # Get data from result as list of tuple 34 | data = result.fetch_all() 35 | print(data) 36 | # output: 37 | # [(datetime.datetime(2022, 4, 27, 9, 4, 25, 367000), 23.5, 1), (datetime.datetime(2022, 4, 27, 9, 5, 25, 367000), 23.5, 1), (datetime.datetime(2022, 4, 27, 9, 6, 25, 367000), 24.399999618530273, 1)] 38 | 39 | # Or get data from result as a list of dict 40 | # map_data = result.fetch_all_into_dict() 41 | # print(map_data) 42 | # output: 43 | # [{'ts': datetime.datetime(2022, 4, 27, 9, 1, 15, 343000), 'temperature': 23.5, 'location': 1}, {'ts': datetime.datetime(2022, 4, 27, 9, 2, 15, 343000), 'temperature': 23.5, 'location': 1}, {'ts': datetime.datetime(2022, 4, 27, 9, 3, 15, 343000), 'temperature': 24.399999618530273, 'location': 1}] 44 | # ANCHOR_END: query 45 | 46 | 47 | conn.close() 48 | -------------------------------------------------------------------------------- /taosrest/__init__.py: -------------------------------------------------------------------------------- 1 | from .connection import TaosRestConnection, Result 2 | from .cursor import TaosRestCursor 3 | from .restclient import RestClient 4 | from .errors import * 5 | 6 | 7 | def connect(**kwargs) -> TaosRestConnection: 8 | """ 9 | Keyword Arguments 10 | ---------------------------- 11 | - url: str, optional, default "http://localhost:6041" 12 | url to connect 13 | - token: str, optional, default None 14 | TDengine cloud Token, which is required only by TDengine cloud service 15 | - user : str, optional, default root 16 | username used to log in 17 | - password : str, optional, default taosdata 18 | password used to log in 19 | - database : str, optional, default None 20 | default database to use. 21 | - timeout : int, optional. 22 | the optional timeout parameter specifies a timeout in seconds for blocking operations 23 | - convert_timestamp: bool, optional, default true 24 | whether to convert timestamp in RFC3339 format to python datatime. 25 | - timezone: str | datetime.tzinfo, optional, default None. 26 | When convert_timestamp is true, which timezone to used. 27 | When the type of timezone is str, it should be recognized by [pytz package](https://pypi.org/project/pytz/). 28 | When the timezone is None, system timezone will be used and the returned datetime object will be offset-naive (no tzinfo), otherwise the returned datetime will be offset-aware(with tzinfo) 29 | Examples 30 | ----------------------------- 31 | connect to cloud service 32 | ```python 33 | import taosrest, os 34 | url = os.environ("TDENGINE_CLOUD_URL") 35 | token = os.environ("TDENGINE_ClOUD_TOKEN") 36 | conn = taosrest.connect(url=url, token=token) 37 | ``` 38 | connect to local taosAdapter 39 | ```python 40 | import taosrest 41 | conn = taosrest.connect(url="http://localhost:6041") 42 | ``` 43 | """ 44 | return TaosRestConnection(**kwargs) 45 | -------------------------------------------------------------------------------- /taos-ws-py/examples/cursor_execute.py: -------------------------------------------------------------------------------- 1 | import taosws 2 | import datetime 3 | 4 | env = { 5 | "db_protocol": "taosws", 6 | "db_user": "root", 7 | "db_pass": "taosdata", 8 | "db_host": "localhost", 9 | "db_port": 6041, 10 | "db_name": "t_ws", 11 | } 12 | 13 | 14 | def make_context(config): 15 | db_protocol = config["db_protocol"] 16 | db_user = config["db_user"] 17 | db_pass = config["db_pass"] 18 | db_host = config["db_host"] 19 | db_port = config["db_port"] 20 | 21 | db_url = f"{db_protocol}://{db_user}:{db_pass}@{db_host}:{db_port}" 22 | 23 | db_name = config["db_name"] 24 | 25 | conn = taosws.connect(db_url) 26 | 27 | return conn, db_name 28 | 29 | 30 | def cursor_execute(): 31 | conn, db = make_context(env) 32 | cur = conn.cursor() 33 | res = cur.execute("show dnodes", 1) 34 | print(f"res: {res}") 35 | cur.execute(f"drop database if exists {db}") 36 | cur.execute(f"create database {db}") 37 | cur.execute("use {name}", name=db) 38 | cur.execute("create stable stb (ts timestamp, v1 int) tags(t1 int)") 39 | 40 | data = [ 41 | { 42 | "name": "tb1", 43 | "t1": 1, 44 | }, 45 | { 46 | "name": "tb2", 47 | "t1": 2, 48 | }, 49 | { 50 | "name": "tb3", 51 | "t1": 3, 52 | }, 53 | ] 54 | 55 | res = cur.execute_many( 56 | "create table {name} using stb tags({t1})", 57 | data, 58 | ) 59 | print(f"res: {res}") 60 | 61 | ts = datetime.datetime.now().astimezone() 62 | data = [ 63 | ("tb1", ts, 1), 64 | ("tb2", ts, 2), 65 | ("tb3", ts, 3), 66 | ] 67 | cur.execute_many( 68 | "insert into {} values('{}', {})", 69 | data, 70 | ) 71 | cur.execute("select * from stb") 72 | row = cur.fetchone() 73 | print(f"row: {row}") 74 | cur.close() 75 | conn.close() 76 | 77 | 78 | if __name__ == "__main__": 79 | cursor_execute() 80 | -------------------------------------------------------------------------------- /taos/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding:UTF-8 2 | from taos.bind import * 3 | from taos.bind2 import * 4 | from taos.connection import TaosConnection 5 | from taos.cursor import * 6 | 7 | # For some reason, the following is needed for VS Code (through PyLance) to 8 | # recognize that "error" is a valid module of the "taos" package. 9 | from taos.error import * 10 | from taos.field import * 11 | from taos.result import * 12 | from taos.schemaless import * 13 | from taos.statement import * 14 | from taos.statement2 import * 15 | from taos.subscription import * 16 | 17 | try: 18 | from taos.sqlalchemy import * 19 | except: 20 | pass 21 | 22 | from taos._version import __version__ 23 | 24 | # Globals 25 | threadsafety = 2 26 | """sqlalchemy will read this attribute""" 27 | paramstyle = "qmark" 28 | """sqlalchemy will read this attribute""" 29 | 30 | __all__ = [ 31 | "__version__", 32 | "IS_V3", 33 | "IGNORE", 34 | "TAOS_FIELD_COL", 35 | "TAOS_FIELD_TAG", 36 | "TAOS_FIELD_QUERY", 37 | "TAOS_FIELD_TBNAME", 38 | # functions 39 | "connect", 40 | "new_bind_param", 41 | "new_bind_params", 42 | "new_multi_binds", 43 | "new_multi_bind", 44 | # objects 45 | "TaosBind", 46 | "TaosConnection", 47 | "TaosCursor", 48 | "TaosResult", 49 | "TaosRows", 50 | "TaosRow", 51 | "TaosStmt", 52 | "TaosStmt2", 53 | "TaosStmt2Option", 54 | "BindTable", 55 | "PrecisionEnum", 56 | "SmlPrecision", 57 | "SmlProtocol", 58 | "utils", 59 | ] 60 | 61 | 62 | def connect(*args, **kwargs): 63 | # type: (..., ...) -> TaosConnection 64 | """Function to return a TDengine connector object 65 | 66 | Current supporting keyword parameters: 67 | @dsn: Data source name as string 68 | @user: Username as string(optional) 69 | @password: Password as string(optional) 70 | @host: Hostname(optional) 71 | @database: Database name(optional) 72 | 73 | @rtype: TDengineConnector 74 | """ 75 | return TaosConnection(*args, **kwargs) 76 | -------------------------------------------------------------------------------- /taos-ws-py/examples/connection_execute.py: -------------------------------------------------------------------------------- 1 | import taosws 2 | import datetime 3 | 4 | env = { 5 | "db_protocol": "taosws", 6 | "db_user": "root", 7 | "db_pass": "taosdata", 8 | "db_host": "localhost", 9 | "db_port": 6041, 10 | "db_name": "t_ws", 11 | } 12 | 13 | 14 | def make_context(config): 15 | db_protocol = config["db_protocol"] 16 | db_user = config["db_user"] 17 | db_pass = config["db_pass"] 18 | db_host = config["db_host"] 19 | db_port = config["db_port"] 20 | 21 | db_url = f"{db_protocol}://{db_user}:{db_pass}@{db_host}:{db_port}" 22 | 23 | db_name = config["db_name"] 24 | 25 | conn = taosws.connect(db_url) 26 | 27 | return conn, db_name 28 | 29 | 30 | def execute(): 31 | conn, db = make_context(env) 32 | res = conn.execute("show dnodes") 33 | print(f"res: {res}") 34 | conn.execute(f"drop database if exists {db}") 35 | conn.execute(f"create database {db}") 36 | conn.execute(f"use {db}") 37 | conn.execute("create stable stb (ts timestamp, v1 int) tags(t1 int)") 38 | 39 | data = [ 40 | { 41 | "name": "tb1", 42 | "t1": 1, 43 | }, 44 | { 45 | "name": "tb2", 46 | "t1": 2, 47 | }, 48 | { 49 | "name": "tb3", 50 | "t1": 3, 51 | }, 52 | ] 53 | 54 | for d in data: 55 | res = conn.execute( 56 | f"create table {d.get('name')} using stb tags({d.get('t1')})", 57 | ) 58 | print(f"res: {res}") 59 | 60 | ts = datetime.datetime.now().astimezone() 61 | data = [ 62 | ("tb1", ts, 1), 63 | ("tb2", ts, 2), 64 | ("tb3", ts, 3), 65 | ] 66 | 67 | for d in data: 68 | res = conn.execute( 69 | f"insert into {d[0]} values('{d[1]}', {d[2]})", 70 | ) 71 | print(f"res: {res}") 72 | 73 | row = conn.execute("select * from stb") 74 | print(f"row: {row}") 75 | conn.close() 76 | 77 | 78 | if __name__ == "__main__": 79 | execute() 80 | -------------------------------------------------------------------------------- /tests/test_query_blob.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from utils import tear_down_database 4 | from taos import utils, IS_V3 5 | from taos.error import InterfaceError 6 | import taos 7 | 8 | 9 | def test_query(): 10 | """This test will use fetch_block for rows fetching, significantly faster than rows_iter""" 11 | conn = taos.connect() 12 | conn.execute("drop database if exists test_query_py") 13 | conn.execute("create database if not exists test_query_py") 14 | conn.execute("use test_query_py") 15 | conn.execute("create table if not exists tb1 (ts timestamp, v blob) tags(jt json)") 16 | n = conn.execute('insert into tn1 using tb1 tags(\'{"name":"value"}\') values(now, null)') 17 | print("inserted %d rows" % n) 18 | result = conn.query("select * from tb1") 19 | fields = result.fields 20 | print("fields: ", fields) 21 | assert fields.count == 3 22 | 23 | results = result.fetch_all() 24 | print("results: ", results) 25 | n = conn.execute('insert into tn1 using tb1 tags(\'{"name":"value"}\') values(now + 10s, "xxxxxxxxxxxxxxxxxxx")') 26 | print("inserted %d rows" % n) 27 | result = conn.query("select * from tb1") 28 | fields = result.fields 29 | print("fields: ", fields) 30 | assert fields.count == 3 31 | results = result.fetch_all() 32 | print("results: ", results) 33 | 34 | n = conn.execute('insert into tn1 using tb1 tags(\'{"name":"value"}\') values(now + 20s, "\\x7f8290")') 35 | print("inserted %d rows" % n) 36 | result = conn.query("select * from tb1") 37 | fields = result.fields 38 | print("fields: ", fields) 39 | assert fields.count == 3 40 | results = result.fetch_all() 41 | print("results: ", results) 42 | assert results[0][1] == None 43 | assert results[1][1] == b"xxxxxxxxxxxxxxxxxxx" 44 | assert results[2][1] == b"\x7f\x82\x90" 45 | assert len(results) == 3 46 | 47 | result.close() 48 | db_name = "test_query_py" 49 | tear_down_database(conn, db_name) 50 | conn.close() 51 | 52 | 53 | if __name__ == "__main__": 54 | test_query() 55 | -------------------------------------------------------------------------------- /tests/test-stmt-insert-multi-nullable.py: -------------------------------------------------------------------------------- 1 | from taos import * 2 | import taos 3 | import pytest 4 | import time 5 | from random import random 6 | 7 | 8 | @pytest.fixture 9 | def conn(): 10 | return taos.connect() 11 | 12 | 13 | def test_stmt_insert_multi_nullable(conn): 14 | dbname = "pytest_taos_stmt_multi" 15 | conn.execute("drop database if exists %s" % dbname) 16 | conn.execute("create database if not exists %s" % dbname) 17 | conn.select_db(dbname) 18 | 19 | conn.execute( 20 | "create table if not exists log(ts timestamp, bo bool, nil tinyint, \ 21 | ti tinyint, si smallint, ii int, bi bigint, tu tinyint unsigned, \ 22 | su smallint unsigned, iu int unsigned, bu bigint unsigned, \ 23 | ff float, dd double, bb binary(100), nn nchar(100), tt timestamp)", 24 | ) 25 | 26 | stmt = conn.statement("insert into log values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)") 27 | 28 | params = new_multi_binds(16) 29 | params[0].timestamp((1626861392589, 1626861392590, 1626861392591)) 30 | params[1].bool((True, None, False)) 31 | params[2].tinyint([-128, -128, None]) # -128 is tinyint null 32 | params[3].tinyint([0, 127, None]) 33 | params[4].smallint([3, None, 2]) 34 | params[5].int([3, 4, None]) 35 | params[6].bigint([3, 4, None]) 36 | params[7].tinyint_unsigned([3, 4, None]) 37 | params[8].smallint_unsigned([3, 4, None]) 38 | params[9].int_unsigned([3, 4, None]) 39 | params[10].bigint_unsigned([3, 4, None]) 40 | params[11].float([3, None, 1]) 41 | params[12].double([3, None, 1.2]) 42 | params[13].binary(["abc", "dddafadfadfadfadfa", None]) 43 | # params[14].nchar(["涛思数据", None, "a long string with 中文字符"]) 44 | params[14].nchar([None, None, None]) 45 | params[15].timestamp([None, None, 1626861392591]) 46 | stmt.bind_param_batch(params) 47 | stmt.execute() 48 | 49 | assert stmt.affected_rows == 3 50 | 51 | result = conn.query("select * from log") 52 | for row in result: 53 | print(row) 54 | result.close() 55 | stmt.close() 56 | conn.close() 57 | -------------------------------------------------------------------------------- /examples/rest_query.py: -------------------------------------------------------------------------------- 1 | """ 2 | Query example for RestConnection 3 | """ 4 | 5 | from taosrest import connect, TaosRestConnection, Result 6 | 7 | conn: TaosRestConnection = connect() 8 | result: Result = conn.query("show databases") 9 | 10 | num_of_fields = result.field_count 11 | for field in result.fields: 12 | print(field) 13 | 14 | for row in result: 15 | print(row) 16 | 17 | # {'name': 'name', 'type': 8, 'bytes': 32} 18 | # {'name': 'created_time', 'type': 9, 'bytes': 8} 19 | # {'name': 'ntables', 'type': 4, 'bytes': 4} 20 | # {'name': 'vgroups', 'type': 4, 'bytes': 4} 21 | # {'name': 'replica', 'type': 3, 'bytes': 2} 22 | # {'name': 'quorum', 'type': 3, 'bytes': 2} 23 | # {'name': 'days', 'type': 3, 'bytes': 2} 24 | # {'name': 'keep', 'type': 8, 'bytes': 24} 25 | # {'name': 'cache(MB)', 'type': 4, 'bytes': 4} 26 | # {'name': 'blocks', 'type': 4, 'bytes': 4} 27 | # {'name': 'minrows', 'type': 4, 'bytes': 4} 28 | # {'name': 'maxrows', 'type': 4, 'bytes': 4} 29 | # {'name': 'wallevel', 'type': 2, 'bytes': 1} 30 | # {'name': 'fsync', 'type': 4, 'bytes': 4} 31 | # {'name': 'comp', 'type': 2, 'bytes': 1} 32 | # {'name': 'cachelast', 'type': 2, 'bytes': 1} 33 | # {'name': 'precision', 'type': 8, 'bytes': 3} 34 | # {'name': 'update', 'type': 2, 'bytes': 1} 35 | # {'name': 'status', 'type': 8, 'bytes': 10} 36 | # ['test', datetime.datetime(2022, 6, 13, 9, 38, 15, 290000, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800), '+08:00')), 1, 1, 1, 1, 10, '3650', 16, 6, 100, 4096, 1, 3000, 2, 0, 'ms', 0, 'ready'] 37 | # ['log', datetime.datetime(2022, 3, 26, 15, 54, 26, 997000, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800), '+08:00')), 274, 1, 1, 1, 10, '30', 1, 3, 100, 4096, 1, 3000, 2, 0, 'us', 0, 'ready'] 38 | # ['test2', datetime.datetime(2022, 4, 21, 9, 34, 52, 236000, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800), '+08:00')), 0, 0, 1, 1, 10, '3650', 16, 6, 100, 4096, 1, 3000, 2, 0, 'ms', 0, 'ready'] 39 | # ['power', datetime.datetime(2022, 6, 7, 13, 3, 10, 198000, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800), '+08:00')), 4, 1, 1, 1, 10, '3650', 16, 6, 100, 4096, 1, 3000, 2, 0, 'ms', 0, 'ready'] 40 | -------------------------------------------------------------------------------- /examples/query-async.py: -------------------------------------------------------------------------------- 1 | from taos import * 2 | from ctypes import * 3 | import time 4 | 5 | 6 | def fetch_callback(p_param, p_result, num_of_rows): 7 | print("fetched ", num_of_rows, "rows") 8 | p = cast(p_param, POINTER(Counter)) 9 | result = TaosResult(p_result) 10 | 11 | if num_of_rows == 0: 12 | print("fetching completed") 13 | p.contents.done = True 14 | # should explicitly close the result in fetch completed or cause error 15 | result.close() 16 | return 17 | if num_of_rows < 0: 18 | p.contents.done = True 19 | result.check_error(num_of_rows) 20 | result.close() 21 | return None 22 | 23 | for row in result.rows_iter(num_of_rows): 24 | # print(row) 25 | None 26 | p.contents.count += result.row_count 27 | result.fetch_rows_a(fetch_callback, p_param) 28 | 29 | 30 | def query_callback(p_param, p_result, code): 31 | # type: (c_void_p, c_void_p, c_int) -> None 32 | if p_result is None: 33 | return 34 | result = TaosResult(p_result) 35 | if code == 0: 36 | result.fetch_rows_a(fetch_callback, p_param) 37 | result.check_error(code) 38 | # explicitly close result while query failed 39 | result.close() 40 | 41 | 42 | class Counter(Structure): 43 | _fields_ = [("count", c_int), ("done", c_bool)] 44 | 45 | def __str__(self): 46 | return "{ count: %d, done: %s }" % (self.count, self.done) 47 | 48 | 49 | def test_query(conn): 50 | # type: (TaosConnection) -> None 51 | 52 | conn.execute("create database if not exists power") 53 | conn.execute( 54 | "CREATE STABLE if not exists power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) " 55 | "TAGS (location BINARY(64), groupId INT)" 56 | ) 57 | 58 | counter = Counter(count=0) 59 | conn.query_a("select * from power.meters", query_callback, byref(counter)) 60 | 61 | while not counter.done: 62 | print("wait query callback") 63 | time.sleep(1) 64 | print(counter) 65 | # conn.close() 66 | 67 | 68 | if __name__ == "__main__": 69 | test_query(connect()) 70 | -------------------------------------------------------------------------------- /taos-ws-py/tests/test_consumer.py: -------------------------------------------------------------------------------- 1 | from taosws import Consumer 2 | import taosws 3 | 4 | 5 | def init_topic(): 6 | conn = taosws.connect() 7 | cursor = conn.cursor() 8 | statements = [ 9 | "drop topic if exists test_topic_1", 10 | "drop database if exists test_topic_1", 11 | "create database test_topic_1 wal_retention_period 3600", 12 | "create topic test_topic_1 with meta as database test_topic_1", 13 | "use test_topic_1", 14 | "create table meters(ts timestamp, c1 bool, c2 tinyint, c3 smallint, c4 int, c5 bigint,\ 15 | c6 timestamp, c7 float, c8 double, c9 varchar(10), c10 nchar(16),\ 16 | c11 tinyint unsigned, c12 smallint unsigned, c13 int unsigned, c14 bigint unsigned)\ 17 | tags(t1 int)", 18 | "create table tb0 using meters tags(1000)", 19 | "create table tb1 using meters tags(NULL)", 20 | """insert into tb0 values(now, NULL, NULL, NULL, NULL, NULL, 21 | NULL, NULL, NULL, NULL, NULL, 22 | NULL, NULL, NULL, NULL) 23 | tb1 values(now, true, -2, -3, -4, -5, 24 | '2022-02-02 02:02:02.222', -0.1, -0.12345678910, 'abc 和我', 'Unicode + 涛思', 25 | 254, 65534, 1, 1)""", 26 | ] 27 | for statement in statements: 28 | cursor.execute(statement) 29 | 30 | 31 | def test_comsumer(): 32 | init_topic() 33 | conf = { 34 | "td.connect.websocket.scheme": "ws", 35 | "group.id": "0", 36 | "auto.offset.reset": "earliest", 37 | } 38 | consumer = Consumer(conf) 39 | 40 | consumer.subscribe(["test_topic_1"]) 41 | 42 | while 1: 43 | message = consumer.poll(timeout=1.0) 44 | if message: 45 | id = message.vgroup() 46 | topic = message.topic() 47 | database = message.database() 48 | 49 | for block in message: 50 | nrows = block.nrows() 51 | ncols = block.ncols() 52 | for row in block: 53 | print(row) 54 | values = block.fetchall() 55 | print(nrows, ncols) 56 | else: 57 | break 58 | 59 | consumer.unsubscribe() 60 | consumer.close() 61 | -------------------------------------------------------------------------------- /taos/constants.py: -------------------------------------------------------------------------------- 1 | # encoding:UTF-8 2 | 3 | """Constants in TDengine python 4 | """ 5 | 6 | import ctypes, struct 7 | from enum import Enum 8 | 9 | 10 | class FieldType(object): 11 | """TDengine Field Types""" 12 | 13 | # type_code 14 | C_NULL = 0 15 | C_BOOL = 1 16 | C_TINYINT = 2 17 | C_SMALLINT = 3 18 | C_INT = 4 19 | C_BIGINT = 5 20 | C_FLOAT = 6 21 | C_DOUBLE = 7 22 | C_VARCHAR = 8 23 | C_BINARY = 8 24 | C_TIMESTAMP = 9 25 | C_NCHAR = 10 26 | C_TINYINT_UNSIGNED = 11 27 | C_SMALLINT_UNSIGNED = 12 28 | C_INT_UNSIGNED = 13 29 | C_BIGINT_UNSIGNED = 14 30 | C_JSON = 15 31 | C_VARBINARY = 16 32 | C_DECIMAL = 17 33 | C_BLOB = 18 34 | C_GEOMETRY = 20 35 | C_DECIMAL64 = 21 36 | # NULL value definition 37 | # NOTE: These values should change according to C definition in tsdb.h 38 | C_BOOL_NULL = 0x02 39 | C_TINYINT_NULL = -128 40 | C_TINYINT_UNSIGNED_NULL = 255 41 | C_SMALLINT_NULL = -32768 42 | C_SMALLINT_UNSIGNED_NULL = 65535 43 | C_INT_NULL = -2147483648 44 | C_INT_UNSIGNED_NULL = 4294967295 45 | C_BIGINT_NULL = -9223372036854775808 46 | C_BIGINT_UNSIGNED_NULL = 18446744073709551615 47 | C_FLOAT_NULL = ctypes.c_float(struct.unpack(" int: 14 | if isinstance(dt, str): 15 | dt_naive = datetime.fromisoformat(dt) 16 | elif isinstance(dt, datetime): 17 | dt_naive = dt 18 | else: 19 | raise TypeError(f"dt type error, expected str or datetime type but got {type(dt)}.") 20 | # 21 | if dt_naive.tzinfo is None or dt_naive.tzinfo.utcoffset(dt_naive) is None: 22 | tz = get_tz() 23 | if tz is not None: 24 | dt_aware = dt_naive.replace(tzinfo=tz) 25 | else: 26 | dt_aware = dt_naive 27 | else: 28 | dt_aware = dt_naive.astimezone(timezone.utc) 29 | # 30 | result = int(dt_aware.timestamp()) 31 | return result 32 | 33 | 34 | def datetime_to_timestamp(value, precision, is_null_type=0): 35 | # type: (datetime | float | int | str | c_int64, PrecisionEnum, int) -> c_int64 36 | if is_null_type > 0 or value is None: 37 | return FieldType.C_BIGINT_NULL 38 | 39 | if type(value) is datetime or isinstance(value, str): 40 | utc_timestamp = parse_datetime_to_utc_timestamp(value) 41 | if precision == PrecisionEnum.Milliseconds: 42 | ret_value = c_int64(utc_timestamp * 10**3) 43 | return ret_value 44 | elif precision == PrecisionEnum.Microseconds: 45 | ret_value = c_int64(utc_timestamp * 10**6) 46 | return ret_value 47 | else: 48 | raise PrecisionError("datetime do not support nanosecond precision") 49 | elif type(value) is float: 50 | if precision == PrecisionEnum.Milliseconds: 51 | return c_int64(round(value * 10**3)) 52 | elif precision == PrecisionEnum.Microseconds: 53 | return c_int64(round(value * 10**6)) 54 | else: 55 | raise PrecisionError("time float do not support nanosecond precision") 56 | elif isinstance(value, int) and not isinstance(value, bool): 57 | return c_int64(value) 58 | elif isinstance(value, c_int64): 59 | return value 60 | return FieldType.C_BIGINT_NULL 61 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "taospy" 3 | version = "2.8.6" 4 | description = "The official TDengine Python connector" 5 | authors = ["Taosdata Inc. "] 6 | license = "MIT" 7 | readme = "README.md" 8 | packages = [ 9 | { include = "taos" }, 10 | { include = "taosrest" } 11 | ] 12 | 13 | [tool.poetry.plugins] # Optional super table 14 | 15 | [tool.poetry.plugins."sqlalchemy.dialects"] 16 | "taos" = "taos.sqlalchemy:TaosDialect" 17 | "taosrest" = "taosrest.sqlalchemy:TaosRestDialect" 18 | "taosws" = "taos.sqlalchemy:TaosWsDialect" 19 | 20 | [tool.poetry.dependencies] 21 | python = ">=3.7,<4.0" 22 | pytz = "*" 23 | iso8601 = "1.0.2" 24 | requests = ">=2.27.1" 25 | typing-extensions = ">=4.2.0,<4.15.0" 26 | 27 | 28 | [tool.poetry.dependencies.taos-ws-py] 29 | version = ">=0.3.0" 30 | python = ">=3.7,<4.0" 31 | optional = true 32 | 33 | [tool.poetry.extras] 34 | ws = ["taos-ws-py"] 35 | 36 | [tool.poetry.group.dev] 37 | 38 | [tool.poetry.group.dev.dependencies] 39 | typing = "*" 40 | pytest = [ 41 | { version = "^4.6", python = ">=2.7,<3.0" }, 42 | { version = "^6.2", python = ">=3.7,<4.0" }, 43 | ] 44 | pytest-cov = "^4.0.0" 45 | mypy = { version = "^0.910", python = "^3.6" } 46 | black = [{ version = ">=21.0", python = ">=3.6.2,<4.0" }] 47 | sqlalchemy = { version = "^2.0.0", python = ">=3.7,<4.0" } 48 | pandas = { version = ">=2.1.0", python = ">=3.9,<4.0" } 49 | python-dotenv = { version = "0.20.0" } 50 | toml = { version = ">=0.8.0", python = ">=3.7,<4.0" } 51 | shapely = { version = ">=2.0.0", python = ">=3.7,<4.0" } 52 | 53 | [tool.poetry.group.test] 54 | 55 | [tool.poetry.group.test.dependencies] 56 | typing = "*" 57 | pytest = [ 58 | { version = "^4.6", python = ">=2.7,<3.0" }, 59 | { version = "^6.2", python = ">=3.7,<4.0" }, 60 | ] 61 | pytest-cov = "^4.0.0" 62 | mypy = { version = "^0.910", python = "^3.6" } 63 | black = [{ version = ">=21.0", python = ">=3.6.2,<4.0" }] 64 | sqlalchemy = { version = "^2.0.0", python = ">=3.7,<4.0" } 65 | pandas = { version = ">=2.1.0", python = ">=3.9,<4.0" } 66 | python-dotenv = { version = "0.20.0" } 67 | toml = { version = ">=0.8.0", python = ">=3.7,<4.0" } 68 | shapely = { version = ">=2.0.0", python = ">=3.7,<4.0" } 69 | DBUtils = { version = ">=3.1.1", python = ">=3.7,<4.0" } 70 | 71 | [build-system] 72 | requires = ["poetry-core>=1.0.5"] 73 | build-backend = "poetry.core.masonry.api" 74 | 75 | [tool.black] 76 | line-length = 119 77 | -------------------------------------------------------------------------------- /tests/test_decode_binary.py: -------------------------------------------------------------------------------- 1 | #! 2 | import taos 3 | from taos.tmq import Consumer 4 | 5 | 6 | def before(): 7 | conn = taos.connect() 8 | conn.execute("drop topic if exists test_decode_binary_topic") 9 | conn.execute("drop database if exists test_decode_binary") 10 | conn.execute("create database if not exists test_decode_binary") 11 | conn.execute("use test_decode_binary") 12 | 13 | conn.execute("create table test_decode_binary (ts timestamp, c1 nchar(10), c2 int, c3 binary(50))") 14 | conn.execute("insert into test_decode_binary values ('2020-01-01 00:00:00', 'hello', 1, 'world')") 15 | conn.execute( 16 | "insert into test_decode_binary values ('2020-01-01 00:00:01', 'hello', 2,'\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09')" 17 | ) 18 | 19 | 20 | def after(): 21 | conn = taos.connect() 22 | conn.execute("drop topic if exists test_decode_binary_topic") 23 | conn.execute("drop database if exists test_decode_binary") 24 | 25 | 26 | def test_decode_binary(): 27 | if not taos.IS_V3: 28 | return 29 | before() 30 | conn = taos.connect(decode_binary=False) 31 | result = conn.query("select * from test_decode_binary.test_decode_binary") 32 | vals = result.fetch_all() 33 | assert len(vals) == 2 34 | assert vals[0][1] == "hello" 35 | assert vals[1][1] == "hello" 36 | assert vals[0][3] == b"world" 37 | assert vals[1][3] == b"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09" 38 | after() 39 | 40 | 41 | def test_decode_binary_in_tmq(): 42 | if not taos.IS_V3: 43 | return 44 | before() 45 | conn = taos.connect() 46 | conn.execute( 47 | "create topic if not exists test_decode_binary_topic as select * from test_decode_binary.test_decode_binary" 48 | ) 49 | 50 | consumer = Consumer({"group.id": "1", "decode_binary": False, "auto.offset.reset": "earliest"}) 51 | consumer.subscribe(["test_decode_binary_topic"]) 52 | 53 | try: 54 | res = consumer.poll(1) 55 | vals = res.value() 56 | assert len(vals) == 2 57 | v0 = vals[0].fetchall() 58 | v1 = vals[1].fetchall() 59 | assert v0[0][1] == "hello" 60 | assert v1[0][1] == "hello" 61 | assert v0[0][3] == b"world" 62 | assert v1[0][3] == b"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09" 63 | finally: 64 | consumer.unsubscribe() 65 | consumer.close() 66 | after() 67 | -------------------------------------------------------------------------------- /examples/connect_rest_examples.py: -------------------------------------------------------------------------------- 1 | # ANCHOR: connect 2 | from taosrest import connect, TaosRestConnection, TaosRestCursor 3 | 4 | conn: TaosRestConnection = connect(url="http://localhost:6041", user="root", password="taosdata", timeout=30) 5 | 6 | # ANCHOR_END: connect 7 | # ANCHOR: basic 8 | # create STable 9 | cursor: TaosRestCursor = conn.cursor() 10 | cursor.execute("DROP DATABASE IF EXISTS power") 11 | cursor.execute("CREATE DATABASE power") 12 | cursor.execute( 13 | "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)" 14 | ) 15 | 16 | # insert data 17 | cursor.execute( 18 | """INSERT INTO power.d1001 USING power.meters TAGS('California.SanFrancisco', 2) VALUES ('2018-10-03 14:38:05.000', 10.30000, 219, 0.31000) ('2018-10-03 14:38:15.000', 12.60000, 218, 0.33000) ('2018-10-03 14:38:16.800', 12.30000, 221, 0.31000) 19 | power.d1002 USING power.meters TAGS('California.SanFrancisco', 3) VALUES ('2018-10-03 14:38:16.650', 10.30000, 218, 0.25000) 20 | power.d1003 USING power.meters TAGS('California.LosAngeles', 2) VALUES ('2018-10-03 14:38:05.500', 11.80000, 221, 0.28000) ('2018-10-03 14:38:16.600', 13.40000, 223, 0.29000) 21 | power.d1004 USING power.meters TAGS('California.LosAngeles', 3) VALUES ('2018-10-03 14:38:05.000', 10.80000, 223, 0.29000) ('2018-10-03 14:38:06.500', 11.50000, 221, 0.35000)""" 22 | ) 23 | print("inserted row count:", cursor.rowcount) 24 | 25 | # query data 26 | cursor.execute("SELECT * FROM power.meters LIMIT 3") 27 | # get total rows 28 | print("queried row count:", cursor.rowcount) 29 | # get column names from cursor 30 | column_names = [meta[0] for meta in cursor.description] 31 | # get rows 32 | data: list[tuple] = cursor.fetchall() 33 | print(column_names) 34 | for row in data: 35 | print(row) 36 | 37 | # output: 38 | # inserted row count: 8 39 | # queried row count: 3 40 | # ['ts', 'current', 'voltage', 'phase', 'location', 'groupid'] 41 | # [datetime.datetime(2018, 10, 3, 14, 38, 5, 500000, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800), '+08:00')), 11.8, 221, 0.28, 'california.losangeles', 2] 42 | # [datetime.datetime(2018, 10, 3, 14, 38, 16, 600000, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800), '+08:00')), 13.4, 223, 0.29, 'california.losangeles', 2] 43 | # [datetime.datetime(2018, 10, 3, 14, 38, 5, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800), '+08:00')), 10.8, 223, 0.29, 'california.losangeles', 3] 44 | # ANCHOR_END: basic 45 | -------------------------------------------------------------------------------- /examples/connect_rest_with_req_id_examples.py: -------------------------------------------------------------------------------- 1 | # ANCHOR: connect 2 | from taosrest import connect, TaosRestConnection, TaosRestCursor 3 | 4 | conn = connect(url="http://localhost:6041", user="root", password="taosdata", timeout=30) 5 | 6 | # ANCHOR_END: connect 7 | # ANCHOR: basic 8 | # create STable 9 | cursor = conn.cursor() 10 | cursor.execute("DROP DATABASE IF EXISTS power", req_id=1) 11 | cursor.execute("CREATE DATABASE power", req_id=2) 12 | cursor.execute( 13 | "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)", 14 | req_id=3, 15 | ) 16 | 17 | # insert data 18 | cursor.execute( 19 | """INSERT INTO power.d1001 USING power.meters TAGS('California.SanFrancisco', 2) VALUES ('2018-10-03 14:38:05.000', 10.30000, 219, 0.31000) ('2018-10-03 14:38:15.000', 12.60000, 218, 0.33000) ('2018-10-03 14:38:16.800', 12.30000, 221, 0.31000) 20 | power.d1002 USING power.meters TAGS('California.SanFrancisco', 3) VALUES ('2018-10-03 14:38:16.650', 10.30000, 218, 0.25000) 21 | power.d1003 USING power.meters TAGS('California.LosAngeles', 2) VALUES ('2018-10-03 14:38:05.500', 11.80000, 221, 0.28000) ('2018-10-03 14:38:16.600', 13.40000, 223, 0.29000) 22 | power.d1004 USING power.meters TAGS('California.LosAngeles', 3) VALUES ('2018-10-03 14:38:05.000', 10.80000, 223, 0.29000) ('2018-10-03 14:38:06.500', 11.50000, 221, 0.35000)""", 23 | req_id=4, 24 | ) 25 | print("inserted row count:", cursor.rowcount) 26 | 27 | # query data 28 | cursor.execute("SELECT * FROM power.meters LIMIT 3", req_id=5) 29 | # get total rows 30 | print("queried row count:", cursor.rowcount) 31 | # get column names from cursor 32 | column_names = [meta[0] for meta in cursor.description] 33 | # get rows 34 | data = cursor.fetchall() 35 | print(column_names) 36 | for row in data: 37 | print(row) 38 | 39 | # output: 40 | # inserted row count: 8 41 | # queried row count: 3 42 | # ['ts', 'current', 'voltage', 'phase', 'location', 'groupid'] 43 | # [datetime.datetime(2018, 10, 3, 14, 38, 5, 500000, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800), '+08:00')), 11.8, 221, 0.28, 'california.losangeles', 2] 44 | # [datetime.datetime(2018, 10, 3, 14, 38, 16, 600000, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800), '+08:00')), 13.4, 223, 0.29, 'california.losangeles', 2] 45 | # [datetime.datetime(2018, 10, 3, 14, 38, 5, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800), '+08:00')), 10.8, 223, 0.29, 'california.losangeles', 3] 46 | # ANCHOR_END: basic 47 | -------------------------------------------------------------------------------- /taos-ws-py/tests/test_tmq_assignment.py: -------------------------------------------------------------------------------- 1 | import taosws 2 | 3 | 4 | def pre_test(): 5 | conn = taosws.connect() 6 | conn.execute("drop topic if exists topic_1") 7 | conn.execute("drop database if exists tmq_test") 8 | conn.execute("create database if not exists tmq_test wal_retention_period 3600") 9 | conn.execute("use tmq_test") 10 | conn.execute( 11 | "create table if not exists tb1 (ts timestamp, c1 int, c2 float, c3 binary(10), geo geometry(512), vbinary varbinary(32)) tags(t1 int)" 12 | ) 13 | conn.execute("create topic if not exists topic_1 as select ts, c1, c2, c3, geo, vbinary from tb1") 14 | conn.execute( 15 | "insert into d0 using tb1 tags (0) values (now-2s, 1, 1.0, 'tmq test', 'POINT (4.0 8.0)', '0x7661726332')" 16 | ) 17 | conn.execute( 18 | "insert into d0 using tb1 tags (0) values (now-1s, 2, 2.0, 'tmq test', 'POINT (4.0 8.0)', '0x7661726332')" 19 | ) 20 | conn.execute( 21 | "insert into d0 using tb1 tags (0) values (now, 3, 3.0, 'tmq test', 'POINT (4.0 8.0)', '0x7661726332')" 22 | ) 23 | 24 | 25 | def after_test(): 26 | conn = taosws.connect() 27 | conn.execute("drop topic if exists topic_1") 28 | conn.execute("drop database if exists tmq_test") 29 | 30 | 31 | def test_tmq_assignment(): 32 | pre_test() 33 | 34 | consumer = taosws.Consumer( 35 | conf={ 36 | "td.connect.websocket.scheme": "ws", 37 | "experimental.snapshot.enable": "false", # should disable snapshot 38 | "group.id": "0", 39 | "auto.offset.reset": "earliest", 40 | } 41 | ) 42 | consumer.subscribe(["topic_1"]) 43 | 44 | while 1: 45 | message = consumer.poll(timeout=1.0) 46 | if message: 47 | id = message.vgroup() 48 | topic = message.topic() 49 | database = message.database() 50 | print(f"vgroup: {id}, topic: {topic}, database: {database}") 51 | 52 | for block in message: 53 | nrows = block.nrows() 54 | ncols = block.ncols() 55 | for row in block: 56 | print(row) 57 | values = block.fetchall() 58 | print(f"nrows: {nrows}, ncols: {ncols}, values: {values}") 59 | 60 | consumer.commit(message) 61 | else: 62 | break 63 | 64 | assignments = consumer.assignment() 65 | for assignment in assignments: 66 | assert assignment.topic() == "topic_1" 67 | assert assignment.assignments()[0].offset() >= 0 68 | 69 | consumer.unsubscribe() 70 | -------------------------------------------------------------------------------- /taos-ws-py/examples/stmt_example.py: -------------------------------------------------------------------------------- 1 | #! 2 | import time 3 | 4 | import taosws 5 | 6 | import taos 7 | 8 | 9 | def before_test(db_name): 10 | taos_conn = taos.connect() 11 | taos_conn.execute("drop database if exists %s" % db_name) 12 | taos_conn.execute("create database %s" % db_name) 13 | taos_conn.select_db(db_name) 14 | taos_conn.execute("create table t1 (ts timestamp, a int, b float, c varchar(10))") 15 | taos_conn.execute("create table stb1 (ts timestamp, a int, b float, c varchar(10)) tags (t1 int, t2 binary(10))") 16 | taos_conn.close() 17 | 18 | 19 | def after_test(db_name): 20 | taos_conn = taos.connect() 21 | taos_conn.execute("drop database if exists %s" % db_name) 22 | taos_conn.close() 23 | 24 | 25 | def stmt_insert(): 26 | db_name = "test_ws_stmt_{}".format(int(time.time())) 27 | before_test(db_name) 28 | 29 | conn = taosws.connect("taosws://root:taosdata@localhost:6041/%s" % db_name) 30 | 31 | stmt = conn.statement() 32 | stmt.prepare("insert into t1 values (?, ?, ?, ?)") 33 | 34 | stmt.bind_param( 35 | [ 36 | taosws.millis_timestamps_to_column([1686844900000, 1686844901000, 1686844902000, 1686844903000]), 37 | taosws.ints_to_column([1, 2, 3, 4]), 38 | taosws.floats_to_column([1.1, 2.2, 3.3, 4.4]), 39 | taosws.varchar_to_column(["a", "b", "c", "d"]), 40 | ] 41 | ) 42 | 43 | stmt.add_batch() 44 | rows = stmt.execute() 45 | assert rows == 4 46 | stmt.close() 47 | after_test(db_name) 48 | 49 | 50 | def stmt_insert_into_stable(): 51 | db_name = "test_ws_stmt_{}".format(int(time.time())) 52 | before_test(db_name) 53 | 54 | conn = taosws.connect("taosws://root:taosdata@localhost:6041/%s" % db_name) 55 | 56 | stmt = conn.statement() 57 | stmt.prepare("insert into ? using stb1 tags (?, ?) values (?, ?, ?, ?)") 58 | stmt.set_tbname("stb1_1") 59 | stmt.set_tags( 60 | [ 61 | taosws.int_to_tag(1), 62 | taosws.varchar_to_tag("aaa"), 63 | ] 64 | ) 65 | stmt.bind_param( 66 | [ 67 | taosws.millis_timestamps_to_column([1686844900000, 1686844901000, 1686844902000, 1686844903000]), 68 | taosws.ints_to_column([1, 2, 3, 4]), 69 | taosws.floats_to_column([1.1, 2.2, 3.3, 4.4]), 70 | taosws.varchar_to_column(["a", "b", "c", "d"]), 71 | ] 72 | ) 73 | 74 | stmt.add_batch() 75 | rows = stmt.execute() 76 | assert rows == 4 77 | stmt.close() 78 | after_test(db_name) 79 | 80 | 81 | if __name__ == "__main__": 82 | stmt_insert() 83 | stmt_insert_into_stable() 84 | -------------------------------------------------------------------------------- /taos-ws-py/tests/test_meters.py: -------------------------------------------------------------------------------- 1 | import taosws 2 | 3 | 4 | def test_meters(): 5 | conn = taosws.connect() 6 | cursor = conn.cursor() 7 | statements = [ 8 | "drop topic if exists test", 9 | "drop database if exists test", 10 | "create database test wal_retention_period 3600", 11 | "use test", 12 | "create table meters(ts timestamp, c1 bool, c2 tinyint, c3 smallint, c4 int, c5 bigint,\ 13 | c6 timestamp, c7 float, c8 double, c9 varchar(10), c10 nchar(16),\ 14 | c11 tinyint unsigned, c12 smallint unsigned, c13 int unsigned, c14 bigint unsigned)\ 15 | tags(t1 int)", 16 | "create table tb0 using meters tags(1000)", 17 | "create table tb1 using meters tags(NULL)", 18 | """insert into tb0 values(now, NULL, NULL, NULL, NULL, NULL, 19 | NULL, NULL, NULL, NULL, NULL, 20 | NULL, NULL, NULL, NULL) 21 | tb1 values(now, true, -2, -3, -4, -5, 22 | '2022-02-02 02:02:02.222', -0.1, -0.12345678910, 'abc 和我', 'Unicode + 涛思', 23 | 254, 65534, 1, 1)""", 24 | ] 25 | for statement in statements: 26 | cursor.execute(statement) 27 | 28 | cursor.execute("select * from test.meters") 29 | 30 | # PEP-249 fetchone() method 31 | row = cursor.fetchone() 32 | 33 | # PEP-249 fetchmany([size = Cursor.arraysize]) method() 34 | # 1. fetch one block by default, the block size is not predictable. 35 | many_dynamic = cursor.fetchmany() 36 | # 2. fetch exact (maximum) size of rows, the result may be less than the size limit. 37 | many_fixed = cursor.fetchmany(10000) 38 | 39 | cursor.execute("select * from test.meters") 40 | # all rows in a sequence of tuple 41 | all = cursor.fetchall() 42 | 43 | # by dict 44 | cursor.execute("select * from test.meters limit 1") 45 | all_dict = cursor.fetch_all_into_dict() # same to cursor.fetchallintodict() 46 | # [{'ts': datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800))), 47 | # 'current': 9.880000114440918, 48 | # 'voltage': 114, 49 | # 'phase': 0.3027780055999756, 50 | # 'groupid': 8, 51 | # 'location': 'California.Campbell'}] 52 | 53 | # by iterator. 54 | 55 | # by fetchone loop 56 | cursor.execute("select * from test.meters") 57 | while True: 58 | row = cursor.fetchone() 59 | if row: 60 | print(row) 61 | else: 62 | break 63 | 64 | cursor.execute("select * from test.meters") 65 | results = cursor.fetchall() 66 | for row in results: 67 | print(row) 68 | 69 | cursor.execute("drop database if exists test") 70 | -------------------------------------------------------------------------------- /taos/statement.py: -------------------------------------------------------------------------------- 1 | from taos.cinterface import * 2 | from taos.error import StatementError 3 | from taos.result import TaosResult 4 | 5 | 6 | class TaosStmt(object): 7 | """TDengine STMT interface""" 8 | 9 | def __init__(self, stmt, decode_binary=True): 10 | self._stmt = stmt 11 | self._decode_binary = decode_binary 12 | 13 | def set_tbname(self, name): 14 | """Set table name if needed. 15 | 16 | Note that the set_tbname* method should only used in insert statement 17 | """ 18 | if self._stmt is None: 19 | raise StatementError("Invalid use of set_tbname") 20 | taos_stmt_set_tbname(self._stmt, name) 21 | 22 | def prepare(self, sql): 23 | # type: (str) -> None 24 | taos_stmt_prepare(self._stmt, sql) 25 | 26 | def set_tbname_tags(self, name, tags): 27 | # type: (str, Array[TaosBind]) -> None 28 | """Set table name with tags, tags is array of BindParams""" 29 | if self._stmt is None: 30 | raise StatementError("Invalid use of set_tbname") 31 | taos_stmt_set_tbname_tags(self._stmt, name, tags) 32 | 33 | def bind_param(self, params, add_batch=True): 34 | # type: (Array[TaosBind], bool) -> None 35 | if self._stmt is None: 36 | raise StatementError("Invalid use of stmt") 37 | taos_stmt_bind_param(self._stmt, params) 38 | if add_batch: 39 | taos_stmt_add_batch(self._stmt) 40 | 41 | def bind_param_batch(self, binds, add_batch=True): 42 | # type: (Array[TaosMultiBind], bool) -> None 43 | if self._stmt is None: 44 | raise StatementError("Invalid use of stmt") 45 | taos_stmt_bind_param_batch(self._stmt, binds) 46 | if add_batch: 47 | taos_stmt_add_batch(self._stmt) 48 | 49 | def add_batch(self): 50 | if self._stmt is None: 51 | raise StatementError("Invalid use of stmt") 52 | taos_stmt_add_batch(self._stmt) 53 | 54 | def execute(self): 55 | if self._stmt is None: 56 | raise StatementError("Invalid use of execute") 57 | taos_stmt_execute(self._stmt) 58 | 59 | def use_result(self): 60 | """NOTE: Don't use a stmt result more than once.""" 61 | result = taos_stmt_use_result(self._stmt) 62 | return TaosResult(result, close_after=False, decode_binary=self._decode_binary) 63 | 64 | @property 65 | def affected_rows(self): 66 | # type: () -> int 67 | return taos_stmt_affected_rows(self._stmt) 68 | 69 | def close(self): 70 | """Close stmt.""" 71 | if self._stmt is None: 72 | return 73 | taos_stmt_close(self._stmt) 74 | self._stmt = None 75 | 76 | def __del__(self): 77 | self.close() 78 | -------------------------------------------------------------------------------- /taos-ws-py/tests/test_cursor_geo.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import taosws 3 | import datetime 4 | 5 | config = [ 6 | { 7 | "db_protocol": "taosws", 8 | "db_user": "root", 9 | "db_pass": "taosdata", 10 | "db_host": "localhost", 11 | "db_port": 6041, 12 | "db_name": "t_ws1", 13 | } 14 | ] 15 | 16 | 17 | @pytest.fixture(params=config) 18 | def ctx(request): 19 | db_protocol = request.param["db_protocol"] 20 | db_user = request.param["db_user"] 21 | db_pass = request.param["db_pass"] 22 | db_host = request.param["db_host"] 23 | db_port = request.param["db_port"] 24 | 25 | db_url = f"{db_protocol}://{db_user}:{db_pass}@{db_host}:{db_port}" 26 | 27 | db_name = request.param["db_name"] 28 | 29 | conn = taosws.connect(db_url) 30 | 31 | yield conn, db_name 32 | 33 | conn.execute("DROP DATABASE IF EXISTS %s" % db_name) 34 | conn.close() 35 | 36 | 37 | def test_execute(ctx): 38 | conn, db = ctx 39 | ws = conn 40 | cur = ws.cursor() 41 | res = cur.execute("show dnodes", 1) 42 | print(f"res: {res}") 43 | db = "test11" 44 | cur.execute("drop database if exists {}", db) 45 | cur.execute("create database {}", db) 46 | cur.execute("use {name}", name=db) 47 | cur.execute("create stable stb (ts timestamp, v1 int, geo geometry(512), vbinary varbinary(32)) tags(t1 int)") 48 | 49 | data = [ 50 | { 51 | "name": "tb1", 52 | "t1": 1, 53 | }, 54 | { 55 | "name": "tb2", 56 | "t1": 2, 57 | }, 58 | { 59 | "name": "tb3", 60 | "t1": 3, 61 | }, 62 | ] 63 | 64 | res = cur.execute_many("create table {name} using stb tags({t1})", data) 65 | print(f"res: {res}") 66 | 67 | ts = datetime.datetime.now().astimezone() 68 | data = [ 69 | ("tb1", ts, 1, "POINT (4.0 8.0)", "0x7661726332"), 70 | ("tb2", ts, 2, "POINT (4.0 8.0)", "0x7661726332"), 71 | ("tb3", ts, 3, "POINT (4.0 8.0)", "0x7661726332"), 72 | ] 73 | cur.execute_many( 74 | "insert into {} values('{}', {}, '{}', '{}')", 75 | data, 76 | ) 77 | geo = bytes( 78 | [ 79 | 0x01, 80 | 0x01, 81 | 0x00, 82 | 0x00, 83 | 0x00, 84 | 0x00, 85 | 0x00, 86 | 0x00, 87 | 0x00, 88 | 0x00, 89 | 0x00, 90 | 0x10, 91 | 0x40, 92 | 0x00, 93 | 0x00, 94 | 0x00, 95 | 0x00, 96 | 0x00, 97 | 0x00, 98 | 0x20, 99 | 0x40, 100 | ] 101 | ) 102 | varbinary = b"0x7661726332" 103 | 104 | cur.execute("select * from stb") 105 | while True: 106 | row = cur.fetchone() 107 | if row: 108 | print(f"row: {row}") 109 | assert row[2] == geo 110 | assert row[3] == varbinary 111 | else: 112 | break 113 | -------------------------------------------------------------------------------- /taos-ws-py/tests/test_stmt2.py: -------------------------------------------------------------------------------- 1 | import time 2 | import taosws 3 | 4 | url = "taosws://root:taosdata@localhost:6041/" 5 | 6 | 7 | def before_test(db_name): 8 | conn = taosws.connect() 9 | conn.execute("drop database if exists %s" % db_name) 10 | conn.execute("create database %s" % db_name) 11 | conn.execute("use %s" % db_name) 12 | conn.execute("create table t1 (ts timestamp, a int, b float, c varchar(10))") 13 | conn.execute("create table stb1 (ts timestamp, a int, b float, c varchar(10)) tags (t1 int, t2 binary(10))") 14 | conn.close() 15 | 16 | 17 | def after_test(db_name): 18 | conn = taosws.connect() 19 | conn.execute("drop database if exists %s" % db_name) 20 | conn.close() 21 | 22 | 23 | def stmt2_query(conn, sql): 24 | stmt2 = conn.stmt2_statement() 25 | stmt2.prepare(sql) 26 | pyStmt2Param = taosws.stmt2_bind_param_view( 27 | table_name="", 28 | tags=None, 29 | columns=[ 30 | taosws.ints_to_column([2]), 31 | ], 32 | ) 33 | stmt2.bind([pyStmt2Param]) 34 | stmt2.execute() 35 | result = stmt2.result_set() 36 | answer = [3, 4] 37 | i = 0 38 | for row in result: 39 | assert row[1] == answer[i] 40 | i += 1 41 | 42 | 43 | def test_stmt2_normal(): 44 | db_name = "test_ws_stmt_{}".format(int(time.time())) 45 | before_test(db_name) 46 | 47 | conn = taosws.connect(url + db_name) 48 | 49 | stmt2 = conn.stmt2_statement() 50 | stmt2.prepare("insert into t1 values (?, ?, ?, ?)") 51 | 52 | pyStmt2Param = taosws.stmt2_bind_param_view( 53 | table_name="", 54 | tags=None, 55 | columns=[ 56 | taosws.millis_timestamps_to_column([1686844800000, 1686844801000, 1686844802000, 1686844803000]), 57 | taosws.ints_to_column([1, 2, 3, 4]), 58 | taosws.floats_to_column([1.1, 2.2, 3.3, 4.4]), 59 | taosws.varchar_to_column(["a", "b", "c", "d"]), 60 | ], 61 | ) 62 | 63 | stmt2.bind([pyStmt2Param]) 64 | rows = stmt2.execute() 65 | assert rows == 4 66 | stmt2_query(conn, "select * from t1 where a > ?") 67 | after_test(db_name) 68 | 69 | 70 | def test_stmt2_stable(): 71 | db_name = "test_ws_stmt".format(int(time.time())) 72 | before_test(db_name) 73 | 74 | conn = taosws.connect(url + db_name) 75 | 76 | stmt2 = conn.stmt2_statement() 77 | stmt2.prepare("insert into ? using stb1 tags (?, ?) values (?, ?, ?, ?)") 78 | 79 | pyStmt2Param = taosws.stmt2_bind_param_view( 80 | table_name="stb1_1", 81 | tags=[ 82 | taosws.int_to_tag(1), 83 | taosws.varchar_to_tag("aaa"), 84 | ], 85 | columns=[ 86 | taosws.millis_timestamps_to_column([1686844800000, 1686844801000, 1686844802000, 1686844803000]), 87 | taosws.ints_to_column([1, 2, 3, 4]), 88 | taosws.floats_to_column([1.1, 2.2, 3.3, 4.4]), 89 | taosws.varchar_to_column(["a", "b", "c", "d"]), 90 | ], 91 | ) 92 | 93 | stmt2.bind([pyStmt2Param]) 94 | rows = stmt2.execute() 95 | assert rows == 4 96 | stmt2_query(conn, "select * from stb1 where a > ?") 97 | after_test(db_name) 98 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/python 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=python 3 | 4 | ### Python ### 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | pip-wheel-metadata/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | pytestdebug.log 56 | 57 | # Translations 58 | *.mo 59 | *.pot 60 | 61 | # Django stuff: 62 | *.log 63 | local_settings.py 64 | db.sqlite3 65 | db.sqlite3-journal 66 | 67 | # Flask stuff: 68 | instance/ 69 | .webassets-cache 70 | 71 | # Scrapy stuff: 72 | .scrapy 73 | 74 | # Sphinx documentation 75 | docs/_build/ 76 | doc/_build/ 77 | 78 | # PyBuilder 79 | target/ 80 | 81 | # Jupyter Notebook 82 | .ipynb_checkpoints 83 | 84 | # IPython 85 | profile_default/ 86 | ipython_config.py 87 | 88 | # pyenv 89 | .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | # Pipfile.lock 97 | 98 | # poetry 99 | # poetry.lock 100 | 101 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 102 | __pypackages__/ 103 | 104 | # Celery stuff 105 | celerybeat-schedule 106 | celerybeat.pid 107 | 108 | # SageMath parsed files 109 | *.sage.py 110 | 111 | # Environments 112 | # .env 113 | .env/ 114 | .venv/ 115 | env/ 116 | venv/ 117 | ENV/ 118 | env.bak/ 119 | venv.bak/ 120 | pythonenv* 121 | 122 | # Spyder project settings 123 | .spyderproject 124 | .spyproject 125 | 126 | # Rope project settings 127 | .ropeproject 128 | 129 | # mkdocs documentation 130 | /site 131 | 132 | # mypy 133 | .mypy_cache/ 134 | .dmypy.json 135 | dmypy.json 136 | 137 | # Pyre type checker 138 | .pyre/ 139 | 140 | # pytype static type analyzer 141 | .pytype/ 142 | 143 | # operating system-related files 144 | # file properties cache/storage on macOS 145 | *.DS_Store 146 | # thumbnail cache on Windows 147 | Thumbs.db 148 | 149 | # profiling data 150 | .prof 151 | .history/ 152 | 153 | # End of https://www.toptal.com/developers/gitignore/api/python 154 | setup.py 155 | .devcontainer 156 | .vscode 157 | .idea 158 | docs 159 | *.swp 160 | *~ 161 | -------------------------------------------------------------------------------- /taos/error.py: -------------------------------------------------------------------------------- 1 | # encoding:UTF-8 2 | """Python exceptions 3 | """ 4 | 5 | 6 | class ErrMsg: 7 | STMT2_NULL = "stmt2 object is null" 8 | STMT2_COUNT_NOTMATCH = "count is not match" 9 | 10 | 11 | class Error(Exception): 12 | def __init__(self, msg=None, errno=0xFFFF): 13 | self.msg = msg 14 | self.errno = errno 15 | self._full_msg = "[0x%04x]: %s" % (self.errno & 0xFFFF, self.msg) 16 | 17 | def __str__(self): 18 | return self._full_msg 19 | 20 | 21 | class Warning(Exception): 22 | """Exception raised for important warnings like data truncations while inserting.""" 23 | 24 | pass 25 | 26 | 27 | class InterfaceError(Error): 28 | """Exception raised for errors that are related to the database interface rather than the database itself.""" 29 | 30 | pass 31 | 32 | 33 | class DatabaseError(Error): 34 | """Exception raised for errors that are related to the database.""" 35 | 36 | pass 37 | 38 | 39 | class ConnectionError(Error): 40 | """Exception raised for connection failed""" 41 | 42 | pass 43 | 44 | 45 | class DataError(DatabaseError): 46 | """Exception raised for errors that are due to problems with the processed data like division by zero, numeric value out of range.""" 47 | 48 | pass 49 | 50 | 51 | class OperationalError(DatabaseError): 52 | """Exception raised for errors that are related to the database's operation and not necessarily under the control of the programmer""" 53 | 54 | pass 55 | 56 | 57 | class IntegrityError(DatabaseError): 58 | """Exception raised when the relational integrity of the database is affected.""" 59 | 60 | pass 61 | 62 | 63 | class InternalError(DatabaseError): 64 | """Exception raised when the database encounters an internal error.""" 65 | 66 | pass 67 | 68 | 69 | class ProgrammingError(DatabaseError): 70 | """Exception raised for programming errors.""" 71 | 72 | pass 73 | 74 | 75 | class NotSupportedError(DatabaseError): 76 | """Exception raised in case a method or database API was used which is not supported by the database,.""" 77 | 78 | pass 79 | 80 | 81 | class StatementError(DatabaseError): 82 | """Exception raised in STMT API.""" 83 | 84 | pass 85 | 86 | 87 | class ResultError(DatabaseError): 88 | """Result related APIs.""" 89 | 90 | pass 91 | 92 | 93 | class SchemalessError(DatabaseError): 94 | """taos_schemaless_insert errors.""" 95 | 96 | def __init__(self, msg=None, errno=0xFFFF, affected_rows=0): 97 | DatabaseError.__init__(self, msg, errno) 98 | self.affected_rows = affected_rows 99 | 100 | def __str__(self): 101 | return self._full_msg + "(affected rows: %d)" % self.affected_rows 102 | 103 | # @property 104 | # def affected_rows(self): 105 | # return self.affected_rows 106 | 107 | 108 | class LinesError(DatabaseError): 109 | """taos_insert_lines errors.""" 110 | 111 | pass 112 | 113 | 114 | class TmqError(DatabaseError): 115 | """Exception raise in TMQ API""" 116 | 117 | pass 118 | 119 | 120 | class DataTypeAndRangeError(Error): 121 | """Exception raise in check value datatype and range error API""" 122 | 123 | pass 124 | -------------------------------------------------------------------------------- /tests/test_sqlalchemy_conntect_pool.py: -------------------------------------------------------------------------------- 1 | import taos 2 | from sqlalchemy import create_engine 3 | from sqlalchemy import text 4 | import threading 5 | 6 | engine = create_engine(url="taos://root:taosdata@localhost:6030?timezone=Asia/Shanghai", pool_size=10, max_overflow=20) 7 | 8 | 9 | def init_db(): 10 | conn = None 11 | host = "localhost" 12 | port = 6030 13 | try: 14 | with engine.begin() as conn: 15 | # create database 16 | conn.execute(text("DROP DATABASE IF EXISTS power")) 17 | conn.execute(text("CREATE DATABASE IF NOT EXISTS power")) 18 | print(f"Create database power successfully") 19 | 20 | # create super table 21 | conn.execute( 22 | text( 23 | "CREATE TABLE IF NOT EXISTS power.meters (`ts` TIMESTAMP, `current` FLOAT, `voltage` INT, `phase` FLOAT) TAGS (`groupid` INT, `location` BINARY(64))" 24 | ) 25 | ) 26 | print(f"Create stable power.meters successfully") 27 | 28 | except Exception as err: 29 | print(f"Failed to create db and table, db addrr:{host}:{port} ; ErrMessage:{err}") 30 | raise 31 | 32 | 33 | def taos_insert_sql(i: int): 34 | conn = None 35 | host = "localhost" 36 | port = 6030 37 | try: 38 | with engine.begin() as conn: 39 | sql = text( 40 | f""" 41 | INSERT INTO 42 | power.d1001 USING power.meters (groupid, location) TAGS(2, 'California.SanFrancisco') 43 | VALUES (NOW + {i+1}a, 10.30000, 219, 0.31000) 44 | (NOW + {i+2}a, 12.60000, 218, 0.33000) (NOW + {i+3}a, 12.30000, 221, 0.31000) 45 | power.d1002 USING power.meters (groupid, location) TAGS(3, 'California.SanFrancisco') 46 | VALUES (NOW + {i+1}a, 10.30000, 218, 0.25000) 47 | """ 48 | ) 49 | affectedRows = conn.execute(sql) 50 | print(f"Successfully inserted {affectedRows} rows to power.meters.") 51 | 52 | except Exception as err: 53 | print(f"Failed to insert data to power.meters, db addr:{host}:{port} ; ErrMessage:{err}") 54 | raise 55 | 56 | 57 | # Use connection pool to execute queries 58 | def query_native(sql: str): 59 | conn = None 60 | cursor = None 61 | try: 62 | # Get connection from pool 63 | with engine.begin() as conn: 64 | # Execute SQL 65 | result = conn.execute(text(sql)) 66 | # Get results 67 | data = result.fetchall() 68 | print(f"Query result: {data}") 69 | return data 70 | except Exception as e: 71 | print(f"TDengine query failed: {e}") 72 | raise 73 | 74 | 75 | def test_connection_pool(): 76 | init_db() # Initialize database and tables 77 | 78 | threads = [] 79 | for i in range(5): 80 | t1 = threading.Thread(target=taos_insert_sql, args=(i * 10,)) 81 | t2 = threading.Thread(target=query_native, args=("SELECT * FROM power.meters",)) 82 | threads.extend([t1, t2]) 83 | t1.start() 84 | t2.start() 85 | 86 | for t in threads: 87 | t.join() 88 | 89 | data = query_native("SELECT count(*) FROM power.meters") 90 | assert data[0][0] == 20, "Expected 20 rows in power.meters" 91 | print("All sub-threads completed, main thread ending") 92 | -------------------------------------------------------------------------------- /examples/tmq_assignment.py: -------------------------------------------------------------------------------- 1 | #! 2 | 3 | import taos 4 | from taos.tmq import Consumer 5 | import taosws 6 | 7 | 8 | def prepare(): 9 | conn = taos.connect() 10 | conn.execute("drop topic if exists tmq_assignment_demo_topic") 11 | conn.execute("drop database if exists tmq_assignment_demo_db") 12 | conn.execute("create database if not exists tmq_assignment_demo_db wal_retention_period 3600") 13 | conn.select_db("tmq_assignment_demo_db") 14 | conn.execute( 15 | "create table if not exists tmq_assignment_demo_table (ts timestamp, c1 int, c2 float, c3 binary(10)) tags(t1 int)" 16 | ) 17 | conn.execute( 18 | "create topic if not exists tmq_assignment_demo_topic as select ts, c1, c2, c3 from tmq_assignment_demo_table" 19 | ) 20 | conn.execute("insert into d0 using tmq_assignment_demo_table tags (0) values (now-2s, 1, 1.0, 'tmq test')") 21 | conn.execute("insert into d0 using tmq_assignment_demo_table tags (0) values (now-1s, 2, 2.0, 'tmq test')") 22 | conn.execute("insert into d0 using tmq_assignment_demo_table tags (0) values (now, 3, 3.0, 'tmq test')") 23 | 24 | 25 | def taos_get_assignment_and_seek_demo(): 26 | prepare() 27 | consumer = Consumer( 28 | { 29 | "group.id": "0", 30 | # should disable snapshot, 31 | # otherwise it will cause invalid params error 32 | "experimental.snapshot.enable": "false", 33 | } 34 | ) 35 | consumer.subscribe(["tmq_assignment_demo_topic"]) 36 | 37 | # get topic assignment 38 | assignments = consumer.assignment() 39 | for assignment in assignments: 40 | print(assignment) 41 | 42 | # poll 43 | consumer.poll(1) 44 | consumer.poll(1) 45 | 46 | # get topic assignment again 47 | after_pool_assignments = consumer.assignment() 48 | for assignment in after_pool_assignments: 49 | print(assignment) 50 | 51 | # seek to the beginning 52 | for assignment in assignments: 53 | consumer.seek(assignment) 54 | 55 | # now the assignment should be the same as before poll 56 | assignments = consumer.assignment() 57 | for assignment in assignments: 58 | print(assignment) 59 | 60 | 61 | def taosws_get_assignment_and_seek_demo(): 62 | prepare() 63 | consumer = taosws.Consumer( 64 | conf={ 65 | "td.connect.websocket.scheme": "ws", 66 | # should disable snapshot, 67 | # otherwise it will cause invalid params error 68 | "experimental.snapshot.enable": "false", 69 | "group.id": "0", 70 | } 71 | ) 72 | consumer.subscribe(["tmq_assignment_demo_topic"]) 73 | 74 | # get topic assignment 75 | assignments = consumer.assignment() 76 | for assignment in assignments: 77 | print(assignment.to_string()) 78 | 79 | # poll 80 | consumer.poll(1) 81 | consumer.poll(1) 82 | 83 | # get topic assignment again 84 | after_poll_assignments = consumer.assignment() 85 | for assignment in after_poll_assignments: 86 | print(assignment.to_string()) 87 | 88 | # seek to the beginning 89 | for assignment in assignments: 90 | for a in assignment.assignments(): 91 | consumer.seek(assignment.topic(), a.vg_id(), a.offset()) 92 | 93 | # now the assignment should be the same as before poll 94 | assignments = consumer.assignment() 95 | for assignment in assignments: 96 | print(assignment.to_string()) 97 | -------------------------------------------------------------------------------- /tests/test_bind_base.py: -------------------------------------------------------------------------------- 1 | # encoding:UTF-8 2 | 3 | from datetime import datetime 4 | from dotenv import load_dotenv 5 | 6 | import taos 7 | from taos.cinterface import * 8 | from taos.bind_base import datetime_to_timestamp 9 | 10 | load_dotenv() 11 | 12 | 13 | def test_datetime_to_timestamp_null(): 14 | if not taos.IS_V3: 15 | return 16 | # 17 | dt_str = "2020-01-01 00:00:00" 18 | dt = datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S") 19 | # datetime 20 | assert datetime_to_timestamp(None, taos.PrecisionEnum.Milliseconds) == taos.FieldType.C_BIGINT_NULL 21 | assert datetime_to_timestamp(dt, taos.PrecisionEnum.Milliseconds, 2) == taos.FieldType.C_BIGINT_NULL 22 | print("pass test_datetime_to_timestamp_null") 23 | 24 | 25 | def check_timestamp(dt_str, dt, seconds_diff): 26 | # datetime 27 | assert datetime_to_timestamp(dt, taos.PrecisionEnum.Milliseconds).value == seconds_diff * 10**3 28 | assert datetime_to_timestamp(dt, taos.PrecisionEnum.Microseconds).value == seconds_diff * 10**6 29 | try: 30 | datetime_to_timestamp(dt, 9) 31 | assert 1 == 2 32 | except: 33 | pass 34 | # float 35 | assert datetime_to_timestamp(seconds_diff * 1.0, taos.PrecisionEnum.Milliseconds).value == seconds_diff * 10**3 36 | assert datetime_to_timestamp(seconds_diff * 1.0, taos.PrecisionEnum.Microseconds).value == seconds_diff * 10**6 37 | try: 38 | datetime_to_timestamp(seconds_diff * 1.0, 9) 39 | assert 1 == 2 40 | except: 41 | pass 42 | # int 43 | assert ( 44 | datetime_to_timestamp(seconds_diff, taos.PrecisionEnum.Milliseconds).value 45 | == ctypes.c_int64(seconds_diff).value 46 | ) 47 | # str 48 | assert datetime_to_timestamp(dt_str, taos.PrecisionEnum.Milliseconds).value == seconds_diff * 10**3 49 | assert datetime_to_timestamp(dt_str, taos.PrecisionEnum.Microseconds).value == seconds_diff * 10**6 50 | try: 51 | datetime_to_timestamp(dt_str, 9) 52 | assert 1 == 2 53 | except: 54 | pass 55 | # c_int64 56 | assert ( 57 | datetime_to_timestamp(ctypes.c_int64(seconds_diff), taos.PrecisionEnum.Milliseconds).value 58 | == ctypes.c_int64(seconds_diff).value 59 | ) 60 | # other 61 | assert datetime_to_timestamp(list(), taos.PrecisionEnum.Milliseconds) == FieldType.C_BIGINT_NULL 62 | 63 | 64 | def test_datetime_to_timestamp_default(): 65 | if not taos.IS_V3: 66 | return 67 | # 68 | now = datetime.now() 69 | timezone_info = now.astimezone().tzinfo 70 | print(f"Current timezone: {timezone_info}") 71 | 72 | dt_str = "2020-01-01 00:00:00" 73 | dt = datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S") 74 | taos.field.set_tz(None) 75 | # Time zone: Asia/Shanghai (CST, +0800) 76 | # seconds_diff = 1577808000 77 | seconds_diff = int(dt.timestamp()) 78 | check_timestamp(dt_str, dt, seconds_diff) 79 | print("pass test_datetime_to_timestamp_default") 80 | 81 | 82 | def test_datetime_to_timestamp_set_timezone(): 83 | if not taos.IS_V3: 84 | return 85 | # 86 | import pytz 87 | 88 | dt_str = "2020-01-01 00:00:00" 89 | dt = datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S") 90 | # set timezone 91 | west_eight_zone = pytz.timezone("America/Los_Angeles") 92 | taos.field.set_tz(west_eight_zone) 93 | seconds_diff = 1577865180 94 | check_timestamp(dt_str, dt, seconds_diff) 95 | print("pass test_datetime_to_timestamp_set_timezone") 96 | -------------------------------------------------------------------------------- /.github/workflows/taos-ws-py-compatibility.yml: -------------------------------------------------------------------------------- 1 | name: taos-ws-py-compatibility 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - "3.0" 8 | tags: 9 | - taos-ws-py-v* 10 | pull_request: 11 | branches: 12 | - main 13 | - "3.0" 14 | 15 | env: 16 | PYTHON_VERSION: "3.10" 17 | 18 | jobs: 19 | rebuild: 20 | runs-on: ubuntu-latest 21 | outputs: 22 | status: ${{ steps.set-output.outputs.rebuild }} 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@v4 26 | with: 27 | fetch-depth: 0 28 | 29 | - name: Detect file changes for rebuild 30 | if: "!startsWith(github.ref, 'refs/tags/taos-ws-py')" 31 | id: detect-changes 32 | uses: tj-actions/changed-files@v46 33 | with: 34 | files: | 35 | .github/workflows/taos-ws-py*.yml 36 | taos-ws-py/** 37 | 38 | - name: Set rebuild status 39 | id: set-output 40 | run: | 41 | if [[ "${GITHUB_REF}" == refs/tags/taos-ws-py* ]]; then 42 | echo "rebuild=true" >> $GITHUB_OUTPUT 43 | else 44 | echo "rebuild=${{ steps.detect-changes.outputs.any_changed }}" >> $GITHUB_OUTPUT 45 | fi 46 | 47 | test: 48 | runs-on: ubuntu-latest 49 | needs: rebuild 50 | if: needs.rebuild.outputs.status == 'true' 51 | timeout-minutes: 30 52 | strategy: 53 | matrix: 54 | target: [x86_64] 55 | steps: 56 | - name: Checkout 57 | uses: actions/checkout@v4 58 | 59 | - name: Set up Python 60 | uses: actions/setup-python@v5 61 | with: 62 | python-version: "${{ env.PYTHON_VERSION }}" 63 | architecture: x64 64 | 65 | - name: Get CMake 66 | uses: lukka/get-cmake@latest 67 | with: 68 | cmakeVersion: 3.31.6 69 | 70 | - name: Build taos-ws-py wheel 71 | uses: PyO3/maturin-action@v1 72 | with: 73 | rust-toolchain: stable 74 | target: ${{ matrix.target }} 75 | working-directory: taos-ws-py 76 | manylinux: auto 77 | args: --release --strip 78 | 79 | - name: Install taos-ws-py wheel 80 | run: pip install taos-ws-py --no-index --force-reinstall --find-links taos-ws-py/target/wheels 81 | 82 | - name: Get TDengine 83 | run: wget https://github.com/taosdata/TDengine/releases/download/ver-3.3.6.0/TDengine-server-3.3.6.0-Linux-x64.tar.gz 84 | 85 | - name: Install TDengine 86 | run: | 87 | tar -zxf TDengine-server-3.3.6.0-Linux-x64.tar.gz 88 | cd TDengine-server-3.3.6.0 89 | sudo ./install.sh -e no 90 | 91 | - name: Copy taos.cfg 92 | run: | 93 | sudo mkdir -p /etc/taos 94 | sudo cp ./.github/workflows/taos.cfg /etc/taos/taos.cfg 95 | 96 | - name: Set timezone 97 | run: sudo timedatectl set-timezone Asia/Shanghai 98 | 99 | - name: Start TDengine 100 | run: | 101 | taosd & 102 | taosadapter & 103 | 104 | - name: Run tests 105 | env: 106 | TDENGINE_URL: localhost:6041 107 | WS_CLOUD_URL: ${{ secrets.WS_CLOUD_URL }} 108 | WS_CLOUD_TOKEN: ${{ secrets.WS_CLOUD_TOKEN }} 109 | TEST_TD_3360: "true" 110 | run: | 111 | pip3 install pytest 112 | pytest ./taos-ws-py/tests/ 113 | -------------------------------------------------------------------------------- /taos-ws-py/tests/test_stmt.py: -------------------------------------------------------------------------------- 1 | import time 2 | import taosws 3 | 4 | 5 | def before_test(db_name): 6 | conn = taosws.connect() 7 | conn.execute("drop database if exists %s" % db_name) 8 | conn.execute("create database %s" % db_name) 9 | conn.execute("use %s" % db_name) 10 | conn.execute("create table t1 (ts timestamp, a int, b float, c varchar(10))") 11 | conn.execute("create table stb1 (ts timestamp, a int, b float, c varchar(10)) tags (t1 int, t2 binary(10))") 12 | conn.close() 13 | 14 | 15 | def after_test(db_name): 16 | conn = taosws.connect() 17 | conn.execute("drop database if exists %s" % db_name) 18 | conn.close() 19 | 20 | 21 | def test_stmt_insert(): 22 | db_name = "test_ws_stmt_{}".format(int(time.time())) 23 | before_test(db_name) 24 | 25 | conn = taosws.connect("taosws://root:taosdata@localhost:6041/%s" % db_name) 26 | 27 | stmt = conn.statement() 28 | stmt.prepare("insert into t1 values (?, ?, ?, ?)") 29 | 30 | stmt.bind_param( 31 | [ 32 | taosws.millis_timestamps_to_column([1686844800000, 1686844801000, 1686844802000, 1686844803000]), 33 | taosws.ints_to_column([1, 2, 3, 4]), 34 | taosws.floats_to_column([1.1, 2.2, 3.3, 4.4]), 35 | taosws.varchar_to_column(["a", "b", "c", "d"]), 36 | ] 37 | ) 38 | 39 | stmt.add_batch() 40 | rows = stmt.execute() 41 | assert rows == 4 42 | stmt.close() 43 | after_test(db_name) 44 | 45 | 46 | def test_stmt_insert_into_stable(): 47 | db_name = "test_ws_stmt_{}".format(int(time.time())) 48 | before_test(db_name) 49 | 50 | conn = taosws.connect("taosws://root:taosdata@localhost:6041/%s" % db_name) 51 | 52 | stmt = conn.statement() 53 | stmt.prepare("insert into ? using stb1 tags (?, ?) values (?, ?, ?, ?)") 54 | stmt.set_tbname("stb1_1") 55 | stmt.set_tags( 56 | [ 57 | taosws.int_to_tag(1), 58 | taosws.varchar_to_tag("aaa"), 59 | ] 60 | ) 61 | stmt.bind_param( 62 | [ 63 | taosws.millis_timestamps_to_column([1686844800000, 1686844801000, 1686844802000, 1686844803000]), 64 | taosws.ints_to_column([1, 2, 3, 4]), 65 | taosws.floats_to_column([1.1, 2.2, 3.3, 4.4]), 66 | taosws.varchar_to_column(["a", "b", "c", "d"]), 67 | ] 68 | ) 69 | 70 | stmt.add_batch() 71 | rows = stmt.execute() 72 | assert rows == 4 73 | stmt.close() 74 | after_test(db_name) 75 | 76 | 77 | def test_stmt_insert_with_null(): 78 | db_name = "test_ws_stmt_{}".format(int(time.time())) 79 | before_test(db_name) 80 | 81 | conn = taosws.connect("taosws://root:taosdata@localhost:6041/%s" % db_name) 82 | stmt = conn.statement() 83 | stmt.prepare("insert into ? using stb1 tags (?, ?) values (?, ?, ?, ?)") 84 | stmt.set_tbname("stb1_1") 85 | stmt.set_tags( 86 | [ 87 | taosws.int_to_tag(1), 88 | taosws.varchar_to_tag(None), 89 | ] 90 | ) 91 | stmt.bind_param( 92 | [ 93 | taosws.millis_timestamps_to_column([1686844800000, 1686844801000, 1686844802000, 1686844803000]), 94 | taosws.ints_to_column([1, 2, None, 4]), 95 | taosws.floats_to_column([1.1, 2.2, None, 4.4]), 96 | taosws.varchar_to_column(["a", None, "c", "d"]), 97 | ] 98 | ) 99 | 100 | stmt.add_batch() 101 | rows = stmt.execute() 102 | assert rows == 4 103 | stmt.close() 104 | after_test(db_name) 105 | -------------------------------------------------------------------------------- /examples/native_all_type_query.py: -------------------------------------------------------------------------------- 1 | import taos 2 | 3 | host = "127.0.0.1" 4 | port = 6030 5 | 6 | 7 | def json_tag_example(): 8 | conn = None 9 | try: 10 | conn = taos.connect(host=host, port=port, user="root", password="taosdata") 11 | # create database 12 | rows_affected: int = conn.execute(f"CREATE DATABASE IF NOT EXISTS example_json_tag") 13 | print(f"Create database power successfully") 14 | assert rows_affected == 0 15 | 16 | rows_affected = conn.execute( 17 | "create table if not exists example_json_tag.stb (ts timestamp, v int) tags(jt json)" 18 | ) 19 | print(f"Create stable example_json_tag.stb successfully") 20 | assert rows_affected == 0 21 | 22 | rows_affected = conn.execute( 23 | 'insert into example_json_tag.tb1 using example_json_tag.stb tags(\'{"name":"value"}\') values(now, 1)' 24 | ) 25 | print(f"Successfully inserted {rows_affected} rows to example_json_tag.tb1.") 26 | 27 | result = conn.query("SELECT ts, v, jt FROM example_json_tag.stb limit 100", req_id=1) 28 | for row in result: 29 | print(f"ts: {row[0]}, v: {row[1]}, jt: {row[2]}") 30 | 31 | except Exception as err: 32 | print(f"Failed to execute json_tag_example, db addrr:{host}:{port} ; ErrMessage:{err}") 33 | finally: 34 | if conn: 35 | conn.close() 36 | 37 | 38 | def all_type_example(): 39 | conn = None 40 | try: 41 | conn = taos.connect(host=host, port=port, user="root", password="taosdata") 42 | # create database 43 | rows_affected: int = conn.execute(f"CREATE DATABASE IF NOT EXISTS all_type_example") 44 | print(f"Create database power successfully") 45 | assert rows_affected == 0 46 | 47 | cols = [ 48 | "ts timestamp", 49 | "int_col INT", 50 | "double_col DOUBLE", 51 | "bool_col BOOL", 52 | "binary_col BINARY(100)", 53 | "nchar_col NCHAR(100)", 54 | "varbinary_col VARBINARY(100)", 55 | "geometry_col GEOMETRY(100)", 56 | ] 57 | tags = [ 58 | "int_tag INT", 59 | "double_tag DOUBLE", 60 | "bool_tag BOOL", 61 | "binary_tag BINARY(100)", 62 | "nchar_tag NCHAR(100)", 63 | "varbinary_tag VARBINARY(100)", 64 | "geometry_tag GEOMETRY(100)", 65 | ] 66 | 67 | str_cols = ",".join(cols) 68 | str_tags = ",".join(tags) 69 | print(str_cols, str_tags) 70 | conn.execute("create table IF NOT EXISTS all_type_example.stb(%s) tags(%s)" % (str_cols, str_tags)) 71 | print(f"Create stable all_type_example.stb successfully") 72 | 73 | rows_affected = conn.execute( 74 | "INSERT INTO all_type_example.tb1 using all_type_example.stb tags(1, 1.1, true, 'binary_value', 'nchar_value', '\\x98f46e', 'POINT(100 100)') " 75 | + "values(now, 1, 1.1, true, 'binary_value', 'nchar_value', '\\x98f46e', 'POINT(100 100)')" 76 | ) 77 | print(f"Successfully inserted {rows_affected} rows to all_type_example.tb1.") 78 | 79 | result = conn.query("SELECT * FROM all_type_example.stb limit 100", req_id=1) 80 | 81 | data_dict = result.fetch_all_into_dict() 82 | print(data_dict) 83 | 84 | except Exception as err: 85 | print(f"Failed to create db and table, db addrr:{host}:{port} ; ErrMessage:{err}") 86 | finally: 87 | if conn: 88 | conn.close() 89 | 90 | 91 | if __name__ == "__main__": 92 | json_tag_example() 93 | all_type_example() 94 | -------------------------------------------------------------------------------- /examples/ws_all_type_query.py: -------------------------------------------------------------------------------- 1 | import taosws 2 | 3 | host = "127.0.0.1" 4 | port = 6041 5 | 6 | 7 | def json_tag_example(): 8 | conn = None 9 | try: 10 | conn = taosws.connect(host=host, port=port, user="root", password="taosdata") 11 | # create database 12 | rows_affected: int = conn.execute(f"CREATE DATABASE IF NOT EXISTS example_json_tag") 13 | print(f"Create database power successfully") 14 | assert rows_affected == 0 15 | 16 | rows_affected = conn.execute( 17 | "create table if not exists example_json_tag.stb (ts timestamp, v int) tags(jt json)" 18 | ) 19 | print(f"Create stable example_json_tag.stb successfully") 20 | assert rows_affected == 0 21 | 22 | rows_affected = conn.execute( 23 | 'insert into example_json_tag.tb1 using example_json_tag.stb tags(\'{"name":"value"}\') values(now, 1)' 24 | ) 25 | print(f"Successfully inserted {rows_affected} rows to example_json_tag.tb1.") 26 | 27 | result = conn.query("SELECT ts, v, jt FROM example_json_tag.stb limit 100") 28 | for row in result: 29 | print(f"ts: {row[0]}, v: {row[1]}, jt: {row[2]}") 30 | 31 | except Exception as err: 32 | print(f"Failed to execute json_tag_example, db addrr:{host}:{port} ; ErrMessage:{err}") 33 | finally: 34 | if conn: 35 | conn.close() 36 | 37 | 38 | def all_type_example(): 39 | conn = None 40 | try: 41 | conn = taosws.connect(host=host, port=port, user="root", password="taosdata") 42 | cursor = conn.cursor() 43 | # create database 44 | rows_affected: int = cursor.execute(f"CREATE DATABASE IF NOT EXISTS all_type_example") 45 | print(f"Create database power successfully") 46 | assert rows_affected == 0 47 | 48 | cols = [ 49 | "ts timestamp", 50 | "int_col INT", 51 | "double_col DOUBLE", 52 | "bool_col BOOL", 53 | "binary_col BINARY(100)", 54 | "nchar_col NCHAR(100)", 55 | "varbinary_col VARBINARY(100)", 56 | "geometry_col GEOMETRY(100)", 57 | ] 58 | tags = [ 59 | "int_tag INT", 60 | "double_tag DOUBLE", 61 | "bool_tag BOOL", 62 | "binary_tag BINARY(100)", 63 | "nchar_tag NCHAR(100)", 64 | "varbinary_tag VARBINARY(100)", 65 | "geometry_tag GEOMETRY(100)", 66 | ] 67 | 68 | str_cols = ",".join(cols) 69 | str_tags = ",".join(tags) 70 | print(str_cols, str_tags) 71 | cursor.execute("create table IF NOT EXISTS all_type_example.stb(%s) tags(%s)" % (str_cols, str_tags)) 72 | print(f"Create stable all_type_example.stb successfully") 73 | 74 | rows_affected = cursor.execute( 75 | "INSERT INTO all_type_example.tb1 using all_type_example.stb tags(1, 1.1, true, 'binary_value', 'nchar_value', '\\x98f46e', 'POINT(100 100)') " 76 | + "values(now, 1, 1.1, true, 'binary_value', 'nchar_value', '\\x98f46e', 'POINT(100 100)')" 77 | ) 78 | print(f"Successfully inserted {rows_affected} rows to all_type_example.tb1.") 79 | 80 | cursor.execute("SELECT * FROM all_type_example.stb limit 100") 81 | 82 | data_dict = cursor.fetchallintodict() 83 | print(data_dict) 84 | cursor.close() 85 | except Exception as err: 86 | print(f"Failed to create db and table, db addrr:{host}:{port} ; ErrMessage:{err}") 87 | finally: 88 | if conn: 89 | conn.close() 90 | 91 | 92 | if __name__ == "__main__": 93 | json_tag_example() 94 | all_type_example() 95 | -------------------------------------------------------------------------------- /tests/test_subscribe.py: -------------------------------------------------------------------------------- 1 | from taos.subscription import TaosSubscription 2 | from taos import * 3 | from ctypes import * 4 | import taos 5 | import pytest 6 | import time 7 | from random import random 8 | 9 | 10 | @pytest.fixture 11 | def conn(): 12 | return taos.connect() 13 | 14 | 15 | def v3(conn): 16 | exit(0) 17 | 18 | 19 | def test_subscribe(conn): 20 | # type: (TaosConnection) -> None 21 | if taos.IS_V3: 22 | return 23 | dbname = "pytest_taos_subscribe_callback" 24 | try: 25 | conn.execute("drop database if exists %s" % dbname) 26 | conn.execute("create database if not exists %s" % dbname) 27 | conn.select_db(dbname) 28 | conn.execute("create table if not exists log(ts timestamp, n int)") 29 | for i in range(10): 30 | conn.execute("insert into log values(now, %d)" % i) 31 | 32 | sub = conn.subscribe(True, "test", "select * from log", 1000) 33 | print("# consume from begin") 34 | for ts, n in sub.consume(): 35 | print(ts, n) 36 | 37 | print("# consume new data") 38 | for i in range(5): 39 | conn.execute("insert into log values(now, %d)(now+1s, %d)" % (i, i)) 40 | result = sub.consume() 41 | for ts, n in result: 42 | print(ts, n) 43 | 44 | print("# consume with a stop condition") 45 | for i in range(10): 46 | conn.execute("insert into log values(now, %d)" % int(random() * 10)) 47 | result = sub.consume() 48 | try: 49 | ts, n = next(result) 50 | print(ts, n) 51 | if n > 5: 52 | result.stop_query() 53 | print("## stopped") 54 | break 55 | except StopIteration: 56 | continue 57 | 58 | sub.close() 59 | 60 | conn.execute("drop database if exists %s" % dbname) 61 | conn.close() 62 | except Exception as err: 63 | conn.execute("drop database if exists %s" % dbname) 64 | conn.close() 65 | raise err 66 | 67 | 68 | def subscribe_callback(p_sub, p_result, p_param, errno): 69 | # type: (c_void_p, c_void_p, c_void_p, c_int) -> None 70 | print("callback") 71 | result = TaosResult(c_void_p(p_result)) 72 | result.check_error(errno) 73 | for row in result.rows_iter(): 74 | ts, n = row() 75 | print(ts, n) 76 | 77 | 78 | def test_subscribe_callback(conn): 79 | # type: (TaosConnection) -> None 80 | if taos.IS_V3: 81 | return 82 | dbname = "pytest_taos_subscribe_callback" 83 | try: 84 | conn.execute("drop database if exists %s" % dbname) 85 | conn.execute("create database if not exists %s" % dbname) 86 | conn.execute("use %s" % dbname) 87 | conn.execute("create table if not exists log(ts timestamp, n int)") 88 | 89 | print("# subscribe with callback") 90 | sub = conn.subscribe(False, "test", "select * from log", 1000, subscribe_callback) 91 | 92 | for i in range(10): 93 | conn.execute("insert into log values(now, %d)" % i) 94 | time.sleep(0.7) 95 | sub.close() 96 | 97 | conn.execute("drop database if exists %s" % dbname) 98 | conn.close() 99 | except Exception as err: 100 | conn.execute("drop database if exists %s" % dbname) 101 | conn.close() 102 | raise err 103 | 104 | 105 | def test_sub_error(): 106 | if taos.IS_V3: 107 | return 108 | conn = taos.connect() 109 | 110 | def query_callback(p_sub, p_result, p_param, code): 111 | pass 112 | 113 | restart = True 114 | topic = "topic-test-sub-error" 115 | sql = "select * from wrongdb.log" 116 | interval = 1000 117 | try: 118 | conn.subscribe(restart, topic, sql, interval, query_callback) 119 | assert False 120 | except: 121 | pass 122 | 123 | 124 | if __name__ == "__main__": 125 | test_subscribe(taos.connect()) 126 | test_subscribe_callback(taos.connect()) 127 | -------------------------------------------------------------------------------- /taosrest/cursor.py: -------------------------------------------------------------------------------- 1 | from .errors import * 2 | from .restclient import RestClient 3 | from typing import Optional 4 | 5 | 6 | class TaosRestCursor: 7 | """ 8 | Implement [PEP 249 cursor API](https://peps.python.org/pep-0249/#cursor-objects) 9 | """ 10 | 11 | def __init__(self, client: RestClient): 12 | self._c = client 13 | self.arraysize = 1 14 | self._rowcount = -1 15 | self._response = None 16 | self._index = -1 17 | self._description = None 18 | self._logfile = "" 19 | self._affected_rows = None 20 | 21 | @property 22 | def rowcount(self): 23 | """the number of rows that the last .execute*() produced (for DQL statements like SELECT) or affected (for DML statements like UPDATE or INSERT).""" 24 | return self._rowcount 25 | 26 | @property 27 | def affected_rows(self): 28 | """Return the rowcount of insertion. For SELECT statement, it will be None""" 29 | return self._affected_rows 30 | 31 | @property 32 | def description(self): 33 | """ 34 | Returns 35 | ------- 36 | This read-only attribute is a sequence of 3-item sequences. 37 | Each of these sequences contains information describing one column: 38 | - column_name 39 | - type_name 40 | - internal_size 41 | """ 42 | if self._response is None: 43 | return None 44 | return self._description 45 | 46 | def callproc(self, procname, parameters=None): 47 | raise NotSupportedError() 48 | 49 | def close(self): 50 | pass 51 | 52 | def execute(self, operation: str, parameters=None, req_id: Optional[int] = None): 53 | self._response = None 54 | self._index = -1 55 | self._response = self._c.sql(operation, req_id=req_id) 56 | 57 | if self._logfile: 58 | with open(self._logfile, "a", encoding="utf-8") as logfile: 59 | logfile.write(f"{operation};\n") 60 | 61 | if self._response["column_meta"][0][0] == "affected_rows": 62 | # for INSERT 63 | self._description = self._response["column_meta"] 64 | self._affected_rows = self._response["data"][0][0] 65 | self._rowcount = self._affected_rows 66 | return self._affected_rows 67 | else: 68 | # for SELECT, Show, ... 69 | self._description = self._response["column_meta"] 70 | self._affected_rows = None 71 | self._rowcount = self._response["rows"] 72 | 73 | def log(self, logfile): 74 | self._logfile = logfile 75 | 76 | def executemany(self, operation: str, parameters=None, req_id: Optional[int] = None): 77 | self.execute(operation, parameters, req_id) 78 | 79 | def istype(self, col, datatype): 80 | if datatype.upper().strip() == self._description[col][1]: 81 | return True 82 | if datatype.upper().strip() == "BINARY" and self._description[col][1] == "VARCHAR": 83 | return True 84 | return False 85 | 86 | def get_type(self, col): 87 | if not self._description: 88 | return None 89 | return self._description[col][1] 90 | 91 | def fetchone(self): 92 | if self._response is None: 93 | raise OperationalError("no result to fetch") 94 | self._index += 1 95 | if self._index + 1 > self._response["rows"]: 96 | return None 97 | return self._response["data"][self._index] 98 | 99 | def fetchmany(self): 100 | return self.fetchone() 101 | 102 | def fetchall(self): 103 | if self._response is None: 104 | raise OperationalError("no result to fetch") 105 | start_index = self._index + 1 106 | self._index = self._response["rows"] 107 | return self._response["data"][start_index:] 108 | 109 | def nextset(self): 110 | raise NotSupportedError() 111 | 112 | def setinputsizes(self): 113 | raise NotSupportedError() 114 | 115 | def setoutputsize(self, size, column=None): 116 | raise NotSupportedError() 117 | 118 | def setoutputsize(self, size, column=None): 119 | raise NotSupportedError() 120 | -------------------------------------------------------------------------------- /examples/native_pandas_example.py: -------------------------------------------------------------------------------- 1 | # ANCHOR: connect 2 | from datetime import datetime 3 | 4 | import pandas 5 | from sqlalchemy import create_engine, text 6 | 7 | 8 | def connect(): 9 | """Create a connection to TDengine using SQLAlchemy""" 10 | engine = create_engine(f"taos://root:taosdata@localhost:6030?timezone=Asia/Shanghai") 11 | conn = engine.connect() 12 | print("Connected to TDengine successfully.") 13 | return conn 14 | 15 | 16 | def pandas_to_sql_example(conn): 17 | """Test writing data to TDengine using pandas DataFrame.to_sql() method and verify the results""" 18 | from sqlalchemy.types import Integer, Float, TIMESTAMP, String 19 | 20 | try: 21 | conn.execute(text("CREATE DATABASE IF NOT EXISTS power")) 22 | conn.execute( 23 | text( 24 | "CREATE STABLE IF NOT EXISTS power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)" 25 | ) 26 | ) 27 | conn.execute(text("Use power")) 28 | 29 | data = { 30 | "ts": [1729653691000, "2024-09-19 10:00:00", datetime(2024, 9, 20, 10, 11, 12, 456)], 31 | "current": [11.5, 12.3, 13.7], 32 | "voltage": [220, 230, 240], 33 | "phase": [1.0, 1.1, 1.2], 34 | "location": ["california.losangeles", "california.sandiego", "california.sanfrancisco"], 35 | "groupid": [2, 2, 3], 36 | "tbname": ["california", "sandiego", "xxxx"], 37 | } 38 | df = pandas.DataFrame(data) 39 | rows_affected = df.to_sql( 40 | "meters", 41 | conn, 42 | if_exists="append", 43 | index=False, 44 | dtype={ 45 | "ts": TIMESTAMP, 46 | "current": Float, 47 | "voltage": Integer, 48 | "phase": Float, 49 | "location": String, 50 | "groupid": Integer, 51 | }, 52 | ) 53 | assert rows_affected == 3, f"Expected to insert 3 rows, affected {rows_affected} rows" 54 | except Exception as err: 55 | print(f"Failed to insert data into power.meters, ErrMessage:{err}") 56 | raise err 57 | 58 | 59 | def pandas_read_sql_example(conn): 60 | """Test reading data from TDengine using pandas read_sql() method""" 61 | try: 62 | sql = text("SELECT * FROM power.meters WHERE current > :current AND phase > :phase") 63 | sql_df = pandas.read_sql(sql=sql, con=conn, params={"current": 10, "phase": 1}) 64 | print(sql_df.head(3)) 65 | print("Read data from TDengine successfully.") 66 | except Exception as err: 67 | print(f"Failed to read data from power.meters, ErrMessage:{err}") 68 | raise err 69 | 70 | 71 | def pandas_read_sql_table_example(conn): 72 | """Test reading data from TDengine using pandas read_sql_table() method""" 73 | try: 74 | table_df = pandas.read_sql_table( 75 | table_name="meters", 76 | con=conn, 77 | index_col="ts", 78 | parse_dates=["ts"], 79 | chunksize=1000, # optional, read data rows in chunks 80 | columns=["ts", "current", "voltage", "phase", "location", "groupid"], 81 | ) 82 | 83 | total_rows = 0 84 | 85 | for i, chunk in enumerate(table_df): 86 | print(f"Chunk {i}:") 87 | for index, row in chunk.iterrows(): 88 | total_rows += 1 89 | print(f"行号: {total_rows}") 90 | print(f"时间戳(ts): {index}") 91 | print(f"电流(c1): {row['current']}") 92 | print(f"电压(voltage): {row['voltage']}") 93 | print(f"相位(phase): {row['phase']}") 94 | print(f"位置(location): {row['location']}") 95 | print(f"组ID(groupid): {row['groupid']}") 96 | 97 | print("Read data from TDengine successfully using read_sql_table.") 98 | except Exception as err: 99 | print(f"Failed to read data from power.meters using read_sql_table, ErrMessage:{err}") 100 | raise err 101 | 102 | 103 | if __name__ == "__main__": 104 | conn = connect() 105 | pandas_to_sql_example(conn) 106 | pandas_read_sql_example(conn) 107 | pandas_read_sql_table_example(conn) 108 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | pypi: 10 | name: Publish to PyPI 11 | runs-on: ubuntu-22.04 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v3 15 | 16 | - name: Poetry Setup 17 | uses: abatilo/actions-poetry@v2 18 | with: 19 | poetry-version: 1.2 20 | 21 | - name: Publish to PyPI 22 | env: 23 | PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} 24 | run: | 25 | poetry publish --build -n --username __token__ --password "$PYPI_TOKEN" 26 | 27 | release: 28 | name: GitHub Release 29 | runs-on: ubuntu-22.04 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v3 33 | 34 | - name: Get CMake 35 | uses: lukka/get-cmake@latest 36 | with: 37 | cmakeVersion: 3.31.6 38 | 39 | - name: Build tools 40 | run: | 41 | sudo apt-get update -y 42 | sudo apt-get install -y build-essential 43 | 44 | - name: Setup Python 45 | run: | 46 | sudo apt-get install -y python3 python3-pip python-is-python3 47 | 48 | - name: Poetry Setup 49 | uses: abatilo/actions-poetry@v2 50 | with: 51 | poetry-version: 1.2 52 | 53 | # Poetry cache with default ~.cache/pypoetry directory. 54 | # Update it when `poetry.lock` file changed. 55 | - name: Cache Poetry 56 | id: cache-poetry 57 | uses: actions/cache@v4 58 | with: 59 | path: ~/.cache/pypoetry 60 | key: ubuntu-22.04-poetry-${{ hashFiles('**/poetry.lock') }} 61 | 62 | #---------------------------------------------- 63 | # install dependencies if cache does not exist 64 | #---------------------------------------------- 65 | - name: Install dependencies 66 | if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' 67 | run: | 68 | sudo apt install -y gnome-keyring 69 | pip3 install --upgrade requests 70 | poetry install --no-interaction --with=test --no-root 71 | 72 | #---------------------------------------------- 73 | # install your root project, if required 74 | #---------------------------------------------- 75 | - name: Install library 76 | run: poetry install --no-interaction --with=test 77 | 78 | - name: Set up Go 79 | uses: actions/setup-go@v2 80 | with: 81 | go-version: 1.18 82 | 83 | - name: Build TDengine 84 | run: | 85 | git clone --depth 1 https://github.com/taosdata/TDengine.git -b 2.4 86 | cd TDengine 87 | git submodule update --depth 1 --init --recursive 88 | mkdir build 89 | cd build 90 | cmake ../ -DBUILD_HTTP=false -DBUILD_DEPENDENCY_TESTS=false -DBUILD_JDBC=false -DCMAKE_INSTALL_PREFIX:PATH=`realpath ../../local/` 91 | make -j14 92 | cd ../../ 93 | 94 | - name: Start TDengine 95 | run: | 96 | tree TDengine/build/build/ 97 | export C_INCLUDE_PATH=$PWD/TDengine/build/build/bin 98 | export LD_LIBRARY_PATH=$PWD/TDengine/build/build/lib 99 | mkdir -p /tmp/taos/ 100 | printf "dataDir /tmp/taos\nlogDir /tmp/taos/\n" > /tmp/taos/taos.cfg 101 | ./TDengine/build/build/bin/taosadapter & 102 | ./TDengine/build/build/bin/taosd -c /tmp/taos/ & 103 | 104 | #---------------------------------------------- 105 | # run test suite 106 | #---------------------------------------------- 107 | - name: Test 108 | run: | 109 | export LD_LIBRARY_PATH=$PWD/TDengine/build/build/lib 110 | export TDENGINE_URL=localhost:6041 111 | poetry run pytest tests 112 | - name: Build Artifacts 113 | run: | 114 | poetry build 115 | 116 | - name: Generate Changelog 117 | run: | 118 | ./ci/extract-changelog.sh > ${{ github.workflow }}-CHANGELOG.txt 119 | - name: Release 120 | uses: softprops/action-gh-release@v1 121 | env: 122 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 123 | with: 124 | body_path: ${{ github.workflow }}-CHANGELOG.txt 125 | files: | 126 | dist/* 127 | -------------------------------------------------------------------------------- /taos-ws-py/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Conventional Changelog](https://www.conventionalcommits.org/en/v1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## v0.6.3 - 2025-11-07 9 | 10 | ### Enhancements: 11 | 12 | - Support configuring the response timeout for WebSocket connections (excluding data subscription) via the `read_timeout` connection parameter. 13 | 14 | ## v0.6.2 - 2025-09-18 15 | 16 | ### Bug Fixes: 17 | 18 | - Fix memory leaks 19 | 20 | ## v0.6.1 - 2025-08-14 21 | 22 | ### Features: 23 | 24 | - Support timezone 25 | 26 | ### Bug Fixes: 27 | 28 | - Fix TDengine 2.6 query timeout issue 29 | 30 | ## v0.6.0 - 2025-07-25 31 | 32 | ### Features: 33 | 34 | - **(ws)**: support BLOB data type 35 | 36 | ## v0.5.4 - 2025-07-07 37 | 38 | ### Features: 39 | 40 | - **(ws)**: upgrade Rust connector to support automatic reconnection 41 | 42 | ## v0.5.3 - 2025-06-19 43 | 44 | - **(taos-ws-py)**: Upgrade Rust connector to support IPv6 45 | 46 | ## v0.5.2 - 2025-05-27 47 | 48 | - **(taos-ws-py)**: Optimization of Python connector WebSocket poll 49 | 50 | ## v0.5.1 - 2025-05-16 51 | 52 | ### Features: 53 | 54 | - **(taos-ws-py)**: support stmt2 in python websocket connector (#327) 55 | 56 | ## v0.4.0 - 2025-03-31 57 | 58 | ### Features: 59 | 60 | - support dynamic add tmq attribute for taosws 61 | 62 | ## v0.3.9 - 2025-02-22 63 | 64 | ### Bug Fixes: 65 | 66 | - offset maybe euqal or over 0 67 | - poetry --with:dev compatible old programe 68 | - test_pandas is taospy test case 69 | - **(ws)**: fix cursor fetchmany 70 | 71 | ### Documents: 72 | 73 | - changelog rewrite 74 | 75 | ## v0.3.8 - 2025-01-02 76 | 77 | ### Features: 78 | 79 | - Supported Apache SuperSet with TDengine Cloud Data Source 80 | 81 | ## v0.3.3 - 2024-09-19 82 | 83 | ### Enhancements: 84 | 85 | - add api committed, position 86 | - update cargo taos version 87 | - **(ws)**: add tmq commit_offset 88 | - **(ws)**: add tmq list_topics 89 | 90 | ### Documents: 91 | 92 | - update dev cmd 93 | 94 | ## v0.3.2 - 2023-12-15 95 | 96 | ### Features: 97 | 98 | - **(ws-py)**: support RUST_LOG 99 | 100 | ### Bug Fixes: 101 | 102 | - add decorator 103 | - changelog 104 | - reformat lib 105 | - test case 106 | 107 | ### Enhancements: 108 | 109 | - update taos version 110 | - **(ws-py)**: update taos version 111 | 112 | ### Documents: 113 | 114 | - add set env cmd 115 | - update build cmd 116 | - update build version 117 | 118 | ## v0.3.2 - 2023-12-15 119 | 120 | ### Features: 121 | 122 | - **(ws-py)**: support RUST_LOG 123 | 124 | ### Bug Fixes: 125 | 126 | - add decorator 127 | - changelog 128 | - reformat lib 129 | - test case 130 | 131 | ### Enhancements: 132 | 133 | - update taos version 134 | - **(ws-py)**: update taos version 135 | 136 | ### Documents: 137 | 138 | - add set env cmd 139 | - update build cmd 140 | - update build version 141 | 142 | ## v0.3.2 - 2023-12-15 143 | 144 | ### Features: 145 | 146 | - **(ws-py)**: support RUST_LOG 147 | 148 | ### Bug Fixes: 149 | 150 | - add decorator 151 | - changelog 152 | - reformat lib 153 | - test case 154 | 155 | ### Enhancements: 156 | 157 | - update taos version 158 | - **(ws-py)**: update taos version 159 | 160 | ### Documents: 161 | 162 | - add set env cmd 163 | - update build cmd 164 | - update build version 165 | 166 | ## v0.3.2 - 2023-12-15 167 | 168 | ## v0.3.1 - 2023-10-08 169 | 170 | ### Bug Fixes: 171 | 172 | - rust panic 173 | 174 | ## v0.3.0 - 2023-10-01 175 | 176 | ### Bug Fixes: 177 | 178 | - update cargo version 179 | 180 | ## v0.2.9 - 2023-09-07 181 | 182 | ### Bug Fixes: 183 | 184 | - fix fetch block via ws 185 | 186 | ## v0.2.8 - 2023-09-06 187 | 188 | ### Bug Fixes: 189 | 190 | - return empty list when no data in cursor 191 | 192 | ## v0.2.7 - 2023-09-06 193 | 194 | ## v0.2.6 - 2023-09-05 195 | 196 | ### Bug Fixes: 197 | 198 | - delete default host in taosws 199 | 200 | ## v0.2.5 - 2023-06-25 201 | 202 | ### Enhancements: 203 | 204 | - add unsubscribe fn 205 | - support assignment and seek via ws 206 | - support schemaless via ws 207 | - support stmt via ws 208 | 209 | ### Documents: 210 | 211 | - update cmd 212 | 213 | ## v0.2.4 - 2023-03-29 214 | 215 | ## v0.2.4 - 2023-03-28 216 | 217 | ### Bug Fixes: 218 | 219 | - add default port for taosrest sqlalchemy (#133) 220 | 221 | ### Enhancements: 222 | 223 | - support req id ws (#161) 224 | - TD-19401 support schemaless raw (#159) 225 | 226 | ## v0.2.3 - 2023-02-22 227 | 228 | ### Bug Fixes: 229 | 230 | - fix rest and ws for cloud 231 | -------------------------------------------------------------------------------- /tests/test_query_a.py: -------------------------------------------------------------------------------- 1 | from taos import * 2 | from ctypes import * 3 | import taos 4 | import pytest 5 | import time 6 | from utils import tear_down_database 7 | from taos.error import InterfaceError 8 | 9 | 10 | @pytest.fixture 11 | def conn(): 12 | return taos.connect() 13 | 14 | 15 | def fetch_callback(p_param, p_result, num_of_rows): 16 | print("fetched ", num_of_rows, "rows") 17 | p = cast(p_param, POINTER(Counter)) 18 | result = TaosResult(p_result) 19 | 20 | if num_of_rows == 0: 21 | print("fetching completed") 22 | p.contents.done = True 23 | result.close() 24 | return 25 | if num_of_rows < 0: 26 | p.contents.done = True 27 | result.check_error(num_of_rows) 28 | result.close() 29 | return None 30 | 31 | for row in result.rows_iter(num_of_rows): 32 | # print(row) 33 | None 34 | p.contents.count += result.row_count 35 | result.fetch_rows_a(fetch_callback, p_param) 36 | 37 | 38 | def query_callback(p_param, p_result, code): 39 | # type: (c_void_p, c_void_p, c_int) -> None 40 | if p_result is None: 41 | return 42 | result = TaosResult(p_result) 43 | if code == 0: 44 | result.fetch_rows_a(fetch_callback, p_param) 45 | result.check_error(code) 46 | 47 | 48 | class Counter(Structure): 49 | _fields_ = [("count", c_int), ("done", c_bool)] 50 | 51 | def __str__(self): 52 | return "{ count: %d, done: %s }" % (self.count, self.done) 53 | 54 | 55 | def test_query(conn): 56 | # type: (TaosConnection) -> None 57 | print("ignore test_query... \n") 58 | return 59 | counter = Counter(count=0) 60 | conn.execute("drop database if exists pytestquerya") 61 | conn.execute("create database pytestquerya") 62 | conn.execute("use pytestquerya") 63 | cols = [ 64 | "bool", 65 | "tinyint", 66 | "smallint", 67 | "int", 68 | "bigint", 69 | "tinyint unsigned", 70 | "smallint unsigned", 71 | "int unsigned", 72 | "bigint unsigned", 73 | "float", 74 | "double", 75 | "binary(100)", 76 | "nchar(100)", 77 | ] 78 | s = ",".join("c%d %s" % (i, t) for i, t in enumerate(cols)) 79 | print(s) 80 | conn.execute("create table tb1(ts timestamp, %s)" % s) 81 | for _ in range(100): 82 | s = ",".join("null" for c in cols) 83 | conn.execute("insert into tb1 values(now, %s)" % s) 84 | conn.query_a("select * from tb1", query_callback, byref(counter)) 85 | 86 | while not counter.done: 87 | print("wait query callback") 88 | time.sleep(1) 89 | print(counter) 90 | db_name = "pytestquerya" 91 | tear_down_database(conn, db_name) 92 | conn.close() 93 | 94 | 95 | def test_query_with_req_id(conn): 96 | # type: (TaosConnection) -> None 97 | print("ignore test_query_with_req_id... \n") 98 | return 99 | 100 | db_name = "pytestquerya" 101 | try: 102 | counter = Counter(count=0) 103 | conn.execute("drop database if exists pytestquerya") 104 | conn.execute("create database pytestquerya") 105 | conn.execute("use pytestquerya") 106 | cols = [ 107 | "bool", 108 | "tinyint", 109 | "smallint", 110 | "int", 111 | "bigint", 112 | "tinyint unsigned", 113 | "smallint unsigned", 114 | "int unsigned", 115 | "bigint unsigned", 116 | "float", 117 | "double", 118 | "binary(100)", 119 | "nchar(100)", 120 | ] 121 | s = ",".join("c%d %s" % (i, t) for i, t in enumerate(cols)) 122 | print(s) 123 | conn.execute("create table tb1(ts timestamp, %s)" % s) 124 | for _ in range(100): 125 | s = ",".join("null" for c in cols) 126 | conn.execute("insert into tb1 values(now, %s)" % s) 127 | req_id = utils.gen_req_id() 128 | conn.query_a("select * from tb1", query_callback, byref(counter), req_id) 129 | 130 | while not counter.done: 131 | print("wait query callback") 132 | time.sleep(1) 133 | print(counter) 134 | tear_down_database(conn, db_name) 135 | conn.close() 136 | except InterfaceError as e: 137 | print(e) 138 | tear_down_database(conn, db_name) 139 | conn.close() 140 | except Exception as e: 141 | print(e) 142 | tear_down_database(conn, db_name) 143 | conn.close() 144 | raise e 145 | 146 | 147 | if __name__ == "__main__": 148 | test_query(taos.connect()) 149 | -------------------------------------------------------------------------------- /README-CN.md: -------------------------------------------------------------------------------- 1 | 2 | # TDengine Python Connector 3 | 4 | 5 | [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/taosdata/taos-connector-python/build.yml)](https://github.com/taosdata/taos-connector-python/actions/workflows/build.yml) 6 | [![codecov](https://codecov.io/gh/taosdata/taos-connector-python/branch/main/graph/badge.svg?token=BDANN3DBXS)](https://codecov.io/gh/taosdata/taos-connector-python) 7 | ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/taosdata/taos-connector-python) 8 | ![PyPI](https://img.shields.io/pypi/dm/taospy) 9 | ![GitHub License](https://img.shields.io/github/license/taosdata/taos-connector-python) 10 | [![PyPI](https://img.shields.io/pypi/v/taospy)](https://pypi.org/project/taospy/) 11 |
12 | [![Twitter Follow](https://img.shields.io/twitter/follow/tdenginedb?label=TDengine&style=social)](https://twitter.com/tdenginedb) 13 | [![YouTube Channel](https://img.shields.io/badge/Subscribe_@tdengine--white?logo=youtube&style=social)](https://www.youtube.com/@tdengine) 14 | [![Discord Community](https://img.shields.io/badge/Join_Discord--white?logo=discord&style=social)](https://discord.com/invite/VZdSuUg4pS) 15 | [![LinkedIn](https://img.shields.io/badge/Follow_LinkedIn--white?logo=linkedin&style=social)](https://www.linkedin.com/company/tdengine) 16 | [![StackOverflow](https://img.shields.io/badge/Ask_StackOverflow--white?logo=stackoverflow&style=social&logoColor=orange)](https://stackoverflow.com/questions/tagged/tdengine) 17 | 18 | 简体中文 | [English](./README.md) 19 | 20 | 21 | ## 目录 22 | 23 | - [1. 简介](#1-简介) 24 | - [2. 文档](#2-文档) 25 | - [3. 前置条件](#3-前置条件) 26 | - [4. 构建](#4-构建) 27 | - [5. 测试](#5-测试) 28 | - [5.1 运行测试](#51-运行测试) 29 | - [5.2 添加用例](#52-添加用例) 30 | - [5.3 性能测试](#53-性能测试) 31 | - [6. CI/CD](#6-cicd) 32 | - [7. 提交 Issue](#7-提交-issue) 33 | - [8. 提交 PR](#8-提交-pr) 34 | - [9. 引用](#9-引用) 35 | - [10. 许可证](#10-许可证) 36 | 37 | ## 1. 简介 38 | 39 | `taospy` 是 TDengine 官方 Python 连接器,允许 Python 开发人员访问 TDengine 数据库的应用程序, 支持数据普通写入、无模式写入、参数绑定写入、查询及订阅等功能。 40 | `taospy` 的 API 符合 Python DB API 2.0(PEP-249) 规范,主要由两个模块组成: 41 | 1. `taos` 模块, 它使用 TDengine C 客户端库进行客户端-服务器间通信。 42 | 2. `taorsest` 模块, 它使用 TDengine RESTful API ,API 符合 Python DB API 2.0(PEP-249)规范, 使用此模块,您不需要再安装 TDengine C 客户端库。 43 | 44 | ## 2. 文档 45 | - 使用 `Python 连接器`, 请参考 [开发指南](https://docs.taosdata.com/develop/),包含了应用如何引入 `Python 连接器` 及数据写入、查询及数据订阅等示例。 46 | - 其他参考信息请看 [参考手册](https://docs.taosdata.com/reference/connector/python/),包含了版本历史、数据类型、示例程序汇总、API 说明和常见问题等。 47 | - 本 README 主要是为想自己贡献、编译、测试 `Python 连接器` 的开发者写的。如果要学习 TDengine,可以浏览 [官方文档](https://docs.taosdata.com/)。 48 | 49 | ## 3. 前置条件 50 | 51 | - Python 运行环境 (taospy: Python >= 3.6.2, taos-ws-py: Python >= 3.7)。 52 | - 本地已部署 TDengine,具体步骤请参考 [部署服务端](https://docs.taosdata.com/get-started/package/),且已经启动 taosd 与 taosAdapter。 53 | 54 | ## 4. 构建 55 | 56 | 下载代码库并在根目录中执行以下操作以构建开发环境: 57 | ``` bash 58 | pip3 install -e ./ 59 | ``` 60 | 61 | ## 5. 测试 62 | ### 5.1 运行测试 63 | Python 连接器测试用例使用 `pytest` 测试框架。 64 | 组件 `taospy` 测试用例目录是: tests/ 65 | 组件 `taos-ws-py` 测试用例目录是: taos-ws-py/tests/ 66 | 67 | 搭建本地测试环境及运行测试用例的代码已封装在一个脚本文件中,执行以下命令即可轻松搭建及执行测试用例: 68 | ``` bash 69 | # for taospy 70 | bash ./test_taospy.sh 71 | 72 | # for taos-ws-py 73 | bash ./test_taos-ws-py.sh 74 | ``` 75 | 76 | ### 5.2 添加用例 77 | 您可以添加新测试文件或在现有测试文件中添加符合 `pytest` 标准的测试用例。 78 | 79 | ### 5.3 性能测试 80 | 性能测试还在开发中。 81 | 82 | ## 6. CI/CD 83 | - [Build Workflow](https://github.com/taosdata/taos-connector-python/actions/workflows/build.yml) 84 | - [Code Coverage](https://app.codecov.io/gh/taosdata/taos-connector-python) 85 | 86 | ## 7. 提交 Issue 87 | 我们欢迎提交 [GitHub Issue](https://github.com/taosdata/taos-connector-python/issues/new?template=Blank+issue)。 提交时请说明下面信息: 88 | - 问题描述,是否必现,最好能包含详细调用堆栈。 89 | - Python 连接器版本。 90 | - Python 连接器连接参数(不需要用户名密码)。 91 | - TDengine 服务端版本。 92 | 93 | ## 8. 提交 PR 94 | 我们欢迎开发者一起开发本项目,提交 PR 时请参考下面步骤: 95 | 1. Fork 本项目,请参考 ([how to fork a repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo))。 96 | 2. 从 main 分支创建一个新分支,请使用有意义的分支名称 (`git checkout -b my_branch`)。注意不要直接在 main 分支上修改。 97 | 3. 修改代码,保证所有单元测试通过,并增加新的单元测试验证修改。 98 | 4. 提交修改到远端分支 (`git push origin my_branch`)。 99 | 5. 在 GitHub 上创建一个 Pull Request ([how to create a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request))。 100 | 6. 提交 PR 后,可以通过 [Pull Request](https://github.com/taosdata/taos-connector-python/pulls) 找到自己的 PR,点击对应链接进去可以看到自己 PR CI 是否通过,如果通过会显示 “All checks have passed”。无论 CI 是否通过,都可以点击 “Show all checks” -> “Details” 来查看详细用例日志。 101 | 7. 提交 PR 后,如果 CI 通过,可以在 [codecov](https://app.codecov.io/gh/taosdata/taos-connector-python/pulls) 页面找到自己 PR,看单测覆盖率。 102 | 103 | ## 9. 引用 104 | 105 | - [TDengine 官网](https://www.taosdata.com/) 106 | - [TDengine GitHub](https://github.com/taosdata/TDengine) 107 | 108 | ## 10. 许可证 109 | 110 | [MIT License](./LICENSE) -------------------------------------------------------------------------------- /taos-ws-py/tests/test_req_id.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import taosws 3 | import datetime 4 | 5 | config = [ 6 | { 7 | "db_protocol": "taosws", 8 | "db_user": "root", 9 | "db_pass": "taosdata", 10 | "db_host": "localhost", 11 | "db_port": 6041, 12 | "db_name": "t_ws", 13 | } 14 | ] 15 | 16 | 17 | @pytest.fixture(params=config) 18 | def ctx(request): 19 | db_protocol = request.param["db_protocol"] 20 | db_user = request.param["db_user"] 21 | db_pass = request.param["db_pass"] 22 | db_host = request.param["db_host"] 23 | db_port = request.param["db_port"] 24 | 25 | db_url = f"{db_protocol}://{db_user}:{db_pass}@{db_host}:{db_port}" 26 | 27 | db_name = request.param["db_name"] 28 | 29 | conn = taosws.connect(db_url) 30 | 31 | yield conn, db_name 32 | 33 | conn.execute("DROP DATABASE IF EXISTS %s" % db_name) 34 | conn.close() 35 | 36 | 37 | def test_query_simple(ctx): 38 | conn, db = ctx 39 | res = conn.query("show dnodes") 40 | print(f"res: {res}") 41 | 42 | 43 | def test_execute_with_req_id_simple(ctx): 44 | conn, db = ctx 45 | res = conn.execute_with_req_id("show dnodes", 1) 46 | print(f"res: {res}") 47 | 48 | 49 | def test_cursor_execute_with_req_id_simple(ctx): 50 | conn, db = ctx 51 | cur = conn.cursor() 52 | res = cur.execute_with_req_id("show dnodes", 1) 53 | print(f"res: {res}") 54 | 55 | 56 | def test_cursor_execute_many_with_req_id(ctx): 57 | conn, db = ctx 58 | ws = conn 59 | cur = ws.cursor() 60 | cur.execute("drop database if exists {}", db) 61 | cur.execute("create database {}", db) 62 | cur.execute("use {name}", name=db) 63 | cur.execute("create stable stb (ts timestamp, v1 int) tags(t1 int)") 64 | 65 | data = [ 66 | { 67 | "name": "tb1", 68 | "t1": 1, 69 | }, 70 | { 71 | "name": "tb2", 72 | "t1": 2, 73 | }, 74 | { 75 | "name": "tb3", 76 | "t1": 3, 77 | }, 78 | ] 79 | 80 | res = cur.execute_many_with_req_id( 81 | "create table {name} using stb tags({t1})", 82 | data, 83 | 1, 84 | ) 85 | 86 | print(f"res: {res}") 87 | 88 | 89 | def test_execute(ctx): 90 | conn, db = ctx 91 | ws = conn 92 | cur = ws.cursor() 93 | res = cur.execute("show dnodes", 1) 94 | print(f"res: {res}") 95 | cur.execute("drop database if exists {}", db) 96 | cur.execute("create database {}", db) 97 | cur.execute("use {name}", name=db) 98 | cur.execute("create stable stb (ts timestamp, v1 int) tags(t1 int)") 99 | 100 | data = [ 101 | { 102 | "name": "tb1", 103 | "t1": 1, 104 | }, 105 | { 106 | "name": "tb2", 107 | "t1": 2, 108 | }, 109 | { 110 | "name": "tb3", 111 | "t1": 3, 112 | }, 113 | ] 114 | 115 | res = cur.execute_many("create table {name} using stb tags({t1})", data) 116 | print(f"res: {res}") 117 | 118 | ts = datetime.datetime.now().astimezone() 119 | data = [ 120 | ("tb1", ts, 1), 121 | ("tb2", ts, 2), 122 | ("tb3", ts, 3), 123 | ] 124 | cur.execute_many( 125 | "insert into {} values('{}', {})", 126 | data, 127 | ) 128 | cur.execute("select * from stb") 129 | row = cur.fetchone() 130 | print(f"row: {row}") 131 | assert row is not None 132 | 133 | 134 | def test_execute_with_req_id(ctx): 135 | conn, db = ctx 136 | ws = conn 137 | cur = ws.cursor() 138 | res = cur.execute_with_req_id("show dnodes", 10) 139 | print(f"res: {res}") 140 | cur.execute_with_req_id(f"drop database if exists {db}", req_id=11) 141 | cur.execute_with_req_id(f"create database {db}", req_id=12) 142 | cur.execute_with_req_id("use {name}", name=db, req_id=13) 143 | cur.execute_with_req_id("create stable stb (ts timestamp, v1 int) tags(t1 int)", req_id=14) 144 | 145 | data = [ 146 | { 147 | "name": "tb1", 148 | "t1": 1, 149 | }, 150 | { 151 | "name": "tb2", 152 | "t1": 2, 153 | }, 154 | { 155 | "name": "tb3", 156 | "t1": 3, 157 | }, 158 | ] 159 | 160 | res = cur.execute_many_with_req_id( 161 | "create table {name} using stb tags({t1})", 162 | data, 163 | 1, 164 | ) 165 | print(f"res: {res}") 166 | 167 | ts = datetime.datetime.now().astimezone() 168 | data = [ 169 | ("tb1", ts, 1), 170 | ("tb2", ts, 2), 171 | ("tb3", ts, 3), 172 | ] 173 | cur.execute_many_with_req_id( 174 | "insert into {} values('{}', {})", 175 | data, 176 | 1, 177 | ) 178 | cur.execute_with_req_id("select * from stb", 1) 179 | row = cur.fetchone() 180 | print(f"row: {row}") 181 | assert row is not None 182 | -------------------------------------------------------------------------------- /taos-ws-py/tests/test_tmq.py: -------------------------------------------------------------------------------- 1 | import taosws 2 | from taosws import Consumer 3 | 4 | 5 | def setup(): 6 | conn = taosws.connect("taosws://root:taosdata@localhost:6041") 7 | cursor = conn.cursor() 8 | statements = [ 9 | "drop topic if exists ws_tmq_meta", 10 | "drop database if exists ws_tmq_meta", 11 | "create database ws_tmq_meta wal_retention_period 3600", 12 | "create topic ws_tmq_meta with meta as database ws_tmq_meta", 13 | "use ws_tmq_meta", 14 | "create table stb1(ts timestamp, c1 bool, c2 tinyint, c3 smallint, c4 int, c5 bigint,\ 15 | c6 timestamp, c7 float, c8 double, c9 varchar(10), c10 nchar(16),\ 16 | c11 tinyint unsigned, c12 smallint unsigned, c13 int unsigned, c14 bigint unsigned)\ 17 | tags(t1 int)", 18 | "create table tb0 using stb1 tags(1000)", 19 | "create table tb1 using stb1 tags(NULL)", 20 | """insert into tb0 values(now, NULL, NULL, NULL, NULL, NULL, 21 | NULL, NULL, NULL, NULL, NULL, 22 | NULL, NULL, NULL, NULL) 23 | tb1 values(now, true, -2, -3, -4, -5, 24 | '2022-02-02 02:02:02.222', -0.1, -0.12345678910, 'abc 和我', 'Unicode + 涛思', 25 | 254, 65534, 1, 1)""", 26 | "create table stb2(ts timestamp, c1 bool, c2 tinyint, c3 smallint, c4 int, c5 bigint,\ 27 | c6 timestamp, c7 float, c8 double, c9 varchar(10), c10 nchar(10),\ 28 | c11 tinyint unsigned, c12 smallint unsigned, c13 int unsigned, c14 bigint unsigned)\ 29 | tags(t1 bool, t2 tinyint, t3 smallint, t4 int, t5 bigint,\ 30 | t6 timestamp, t7 float, t8 double, t9 varchar(10), t10 nchar(16),\ 31 | t11 tinyint unsigned, t12 smallint unsigned, t13 int unsigned, t14 bigint unsigned)", 32 | "create table tb2 using stb2 tags(true, -2, -3, -4, -5, \ 33 | '2022-02-02 02:02:02.222', -0.1, -0.12345678910, 'abc 和我', 'Unicode + 涛思',\ 34 | 254, 65534, 1, 1)", 35 | "create table tb3 using stb2 tags( NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 36 | "create table `table` (ts timestamp, v int)", 37 | "alter table stb1 add column new1 bool", 38 | "alter table stb1 add column new2 tinyint", 39 | "alter table stb1 add column new10 nchar(16)", 40 | "alter table stb1 modify column new10 nchar(32)", 41 | "alter table stb1 drop column new10", 42 | "alter table stb1 drop column new2", 43 | "alter table stb1 drop column new1", 44 | "alter table `stb2` add tag new1 bool", 45 | "alter table `stb2` rename tag new1 new1_new", 46 | "alter table `stb2` modify tag t10 nchar(32)", 47 | "alter table `stb2` drop tag new1_new", 48 | "alter table `table` add column new1 bool", 49 | "alter table `table` add column new2 tinyint", 50 | "alter table `table` add column new10 nchar(16)", 51 | "alter table `table` modify column new10 nchar(32)", 52 | "alter table `table` rename column new10 new10_new", 53 | "alter table `table` drop column new10_new", 54 | "alter table `table` drop column new2", 55 | "alter table `table` drop column new1", 56 | ] 57 | for statement in statements: 58 | # print(statement) 59 | cursor.execute(statement) 60 | 61 | 62 | def test_tmq(): 63 | setup() 64 | conf = { 65 | "td.connect.websocket.scheme": "ws", 66 | "group.id": "0", 67 | # 3.3.6.0 support 68 | "fetch.max.wait.ms": "3001", 69 | "min.poll.rows": "129", 70 | } 71 | consumer = Consumer(conf) 72 | 73 | consumer.subscribe(["ws_tmq_meta"]) 74 | 75 | while 1: 76 | message = consumer.poll(timeout=1.0) 77 | if message: 78 | id = message.vgroup() 79 | topic = message.topic() 80 | database = message.database() 81 | print(f"vgroup: {id}, topic: {topic}, database: {database}") 82 | 83 | for block in message: 84 | nrows = block.nrows() 85 | ncols = block.ncols() 86 | for row in block: 87 | print(row) 88 | values = block.fetchall() 89 | print(f"nrows: {nrows}, ncols: {ncols}, values: {values}") 90 | 91 | consumer.commit(message) 92 | else: 93 | break 94 | 95 | consumer.unsubscribe() 96 | 97 | 98 | def show_env(): 99 | import os 100 | 101 | print("-" * 40) 102 | print("TAOS_LIBRARY_PATH: ", os.environ.get("TAOS_LIBRARY_PATH")) 103 | print("TAOS_CONFIG_DIR: ", os.environ.get("TAOS_CONFIG_DIR")) 104 | print("TAOS_C_CLIENT_VERSION: ", os.environ.get("TAOS_C_CLIENT_VERSION")) 105 | print("TAOS_C_CLIENT_VERSION_STR: ", os.environ.get("TAOS_C_CLIENT_VERSION_STR")) 106 | print("taosws.__version__", taosws.__version__) 107 | print("-" * 40) 108 | 109 | 110 | if __name__ == "__main__": 111 | show_env() 112 | print("forbid test_tmq because run long time\n") 113 | # test_tmq() # run for many hours , so foribd for a moment, if found reason then open again. 114 | -------------------------------------------------------------------------------- /taos-ws-py/tests/test_blob.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import time 3 | import taosws 4 | from taosws import Consumer 5 | import os 6 | 7 | 8 | def test_blob_sql(): 9 | value = os.getenv("TEST_TD_3360") 10 | if value is not None: 11 | return 12 | 13 | conn = taosws.connect("ws://localhost:6041") 14 | cursor = conn.cursor() 15 | 16 | try: 17 | cursor.execute("drop database if exists test_1753269319") 18 | cursor.execute("create database test_1753269319") 19 | cursor.execute("use test_1753269319") 20 | cursor.execute("create table t0(ts timestamp, c1 blob)") 21 | cursor.execute("insert into t0 values(1752218982761, null)") 22 | cursor.execute("insert into t0 values(1752218982762, '')") 23 | cursor.execute("insert into t0 values(1752218982763, 'hello')") 24 | cursor.execute("insert into t0 values(1752218982764, '\\x12345678')") 25 | 26 | cursor.execute("select * from t0") 27 | rows = cursor.fetchall() 28 | 29 | assert len(rows) == 4 30 | 31 | assert rows[0][0].timestamp() * 1000 == 1752218982761 32 | assert rows[1][0].timestamp() * 1000 == 1752218982762 33 | assert rows[2][0].timestamp() * 1000 == 1752218982763 34 | assert rows[3][0].timestamp() * 1000 == 1752218982764 35 | 36 | assert rows[0][1] is None 37 | assert rows[1][1] == b"" 38 | assert rows[2][1] == b"hello" 39 | assert rows[3][1] == b"\x124Vx" 40 | 41 | finally: 42 | cursor.execute("drop database test_1753269319") 43 | conn.close() 44 | 45 | 46 | def test_blob_stmt2(): 47 | value = os.getenv("TEST_TD_3360") 48 | if value is not None: 49 | return 50 | 51 | conn = taosws.connect("ws://localhost:6041") 52 | try: 53 | conn.execute("drop database if exists test_1753269333") 54 | conn.execute("create database test_1753269333") 55 | conn.execute("use test_1753269333") 56 | conn.execute("create table t0 (ts timestamp, c1 blob)") 57 | 58 | test_timestamps = [1726803356466, 1726803356467, 1726803356468, 1726803356469] 59 | test_blobs = [None, b"", b"hello", b"\x124Vx"] 60 | 61 | stmt2 = conn.stmt2_statement() 62 | stmt2.prepare("insert into t0 values (?, ?)") 63 | 64 | param = taosws.stmt2_bind_param_view( 65 | table_name="", 66 | tags=None, 67 | columns=[ 68 | taosws.millis_timestamps_to_column(test_timestamps), 69 | taosws.blob_to_column(test_blobs), 70 | ], 71 | ) 72 | stmt2.bind([param]) 73 | 74 | affected_rows = stmt2.execute() 75 | assert affected_rows == 4 76 | 77 | stmt2.prepare("select * from t0 where ts > ?") 78 | 79 | param = taosws.stmt2_bind_param_view( 80 | table_name="", 81 | tags=None, 82 | columns=[taosws.millis_timestamps_to_column([1726803356465])], 83 | ) 84 | stmt2.bind([param]) 85 | stmt2.execute() 86 | 87 | result = stmt2.result_set() 88 | expected_results = list(zip(test_timestamps, test_blobs)) 89 | 90 | actual_results = [] 91 | for row in result: 92 | dt = datetime.datetime.strptime(row[0], "%Y-%m-%d %H:%M:%S.%f %z") 93 | timestamp = int(dt.timestamp() * 1000) 94 | actual_results.append((timestamp, row[1])) 95 | 96 | assert actual_results == expected_results 97 | 98 | finally: 99 | conn.execute("drop database test_1753269333") 100 | conn.close() 101 | 102 | 103 | def test_blob_tmq(): 104 | value = os.getenv("TEST_TD_3360") 105 | if value is not None: 106 | return 107 | 108 | conn = taosws.connect("ws://localhost:6041") 109 | try: 110 | conn.execute("drop topic if exists topic_1753270984") 111 | conn.execute("drop database if exists test_1753270984") 112 | conn.execute("create database test_1753270984") 113 | conn.execute("create topic topic_1753270984 as database test_1753270984") 114 | conn.execute("use test_1753270984") 115 | conn.execute("create table t0 (ts timestamp, c1 int, c2 blob)") 116 | 117 | num = 100 118 | 119 | sql = "insert into t0 values " 120 | for i in range(num): 121 | ts = 1726803356466 + i 122 | sql += f"({ts}, {i}, 'blob_{i}'), " 123 | conn.execute(sql) 124 | 125 | consumer = Consumer(dsn="ws://localhost:6041?group.id=10&auto.offset.reset=earliest") 126 | consumer.subscribe(["topic_1753270984"]) 127 | 128 | cnt = 0 129 | 130 | while 1: 131 | message = consumer.poll(timeout=5.0) 132 | if message: 133 | for block in message: 134 | cnt += block.nrows() 135 | consumer.commit(message) 136 | else: 137 | break 138 | 139 | assert cnt == num 140 | 141 | consumer.unsubscribe() 142 | 143 | finally: 144 | time.sleep(3) 145 | conn.execute("drop topic topic_1753270984") 146 | conn.execute("drop database test_1753270984") 147 | conn.close() 148 | -------------------------------------------------------------------------------- /tests/test_dbutils_conntect_pool.py: -------------------------------------------------------------------------------- 1 | import threading 2 | from dbutils.pooled_db import PooledDB 3 | import taos # TDengine official Python connector (install with: pip install taospy) 4 | 5 | 6 | # Initialize TDengine connection pool 7 | def init_native_pool(): 8 | return PooledDB( 9 | creator=taos.connect, # Fix: Use taos.connect instead of taos 10 | maxconnections=10, # Maximum number of connections 11 | # TDengine connection parameters (modify according to actual environment) 12 | host="localhost", 13 | port=6030, 14 | user="root", 15 | password="taosdata", 16 | ) 17 | 18 | 19 | # Global connection pool instance 20 | native_pool = init_native_pool() 21 | 22 | 23 | def init_db(): 24 | conn = None 25 | host = "localhost" # Fix: Remove comma to avoid tuple 26 | port = 6030 # Fix: Use correct TDengine port 6030 27 | try: 28 | conn = native_pool.connection() 29 | cursor = conn.cursor() 30 | # create database 31 | cursor.execute("DROP DATABASE IF EXISTS power") # Fix: DROP not Drop 32 | rowsAffected = cursor.execute("CREATE DATABASE IF NOT EXISTS power") 33 | print(f"Create database power successfully, rowsAffected: {rowsAffected}") 34 | assert rowsAffected == 0 35 | 36 | # create super table 37 | rowsAffected = cursor.execute( 38 | "CREATE TABLE IF NOT EXISTS power.meters (`ts` TIMESTAMP, `current` FLOAT, `voltage` INT, `phase` FLOAT) TAGS (`groupid` INT, `location` BINARY(64))" 39 | ) 40 | print(f"Create stable power.meters successfully, rowsAffected: {rowsAffected}") # Fix: Remove semicolon 41 | 42 | except Exception as err: 43 | print(f"Failed to create db and table, db addr:{host}:{port} ; ErrMessage:{err}") 44 | finally: 45 | if conn: 46 | conn.close() 47 | 48 | 49 | def taos_insert_sql(i: int): 50 | conn = None 51 | host = "localhost" 52 | port = 6030 53 | try: 54 | conn = native_pool.connection() 55 | cursor = conn.cursor() 56 | sql = f""" 57 | INSERT INTO 58 | power.d1001 USING power.meters (groupid, location) TAGS(2, 'California.SanFrancisco') 59 | VALUES (NOW + {i + 1}a, 10.30000, 219, 0.31000) 60 | (NOW + {i + 2}a, 12.60000, 218, 0.33000) (NOW + {i + 3}a, 12.30000, 221, 0.31000) 61 | power.d1002 USING power.meters (groupid, location) TAGS(3, 'California.SanFrancisco') 62 | VALUES (NOW + {i + 1}a, 10.30000, 218, 0.25000) 63 | """ 64 | affectedRows = cursor.execute(sql) 65 | print(f"Successfully inserted {affectedRows} rows to power.meters.") 66 | 67 | except Exception as err: 68 | print(f"Failed to insert data to power.meters, db addr:{host}:{port} ; ErrMessage:{err}") 69 | raise 70 | finally: 71 | if conn: 72 | conn.close() 73 | 74 | 75 | # Use connection pool to execute queries 76 | def query_native(sql: str): 77 | conn = None 78 | cursor = None 79 | try: 80 | # Get connection from connection pool 81 | conn = native_pool.connection() 82 | # Create cursor 83 | cursor = conn.cursor() 84 | # Execute SQL 85 | cursor.execute(sql) 86 | # Get results 87 | data = cursor.fetchall() 88 | print(f"Query result: {data}") 89 | return data 90 | except Exception as e: 91 | print(f"TDengine query failed: {e}") 92 | raise "TDengine query failed!" 93 | finally: 94 | # Close cursor 95 | if cursor: 96 | cursor.close() 97 | # Return connection to pool (not actually closed, just marked as idle) 98 | if conn: 99 | conn.close() 100 | 101 | 102 | # Example: Query tables in TDengine 103 | def test_connection_pool(): 104 | """Test basic functionality of connection pool""" 105 | init_db() # Initialize database and tables 106 | 107 | # Query database list 108 | result = query_native("SHOW DATABASES") 109 | print("TDengine database list:", result) 110 | 111 | # Multithreaded concurrent test 112 | threads = [] 113 | for i in range(5): 114 | t1 = threading.Thread(target=taos_insert_sql, args=(i * 10,), name=f"Insert-{i}") 115 | # Fix: Use COUNT(*) instead of SELECT * to avoid memory issues 116 | t2 = threading.Thread(target=query_native, args=("SELECT COUNT(*) FROM power.meters",), name=f"Query-{i}") 117 | threads.extend([t1, t2]) 118 | t1.start() 119 | t2.start() 120 | 121 | # Main thread waits for all sub-threads to complete 122 | for t in threads: 123 | t.join() 124 | print(f"Thread {t.name} completed") 125 | 126 | # Final verification 127 | data = query_native("SELECT COUNT(*) FROM power.meters") 128 | if data and len(data) > 0: 129 | count = data[0][0] 130 | print(f"Total records: {count}") 131 | assert count == 20, f"Expected 20 rows in power.meters, but got {count}" 132 | 133 | print("All sub-threads completed, main thread ending") 134 | 135 | 136 | if __name__ == "__main__": 137 | test_connection_pool() 138 | -------------------------------------------------------------------------------- /examples/ws_all_type_stmt.py: -------------------------------------------------------------------------------- 1 | import taosws 2 | 3 | host = "127.0.0.1" 4 | port = 6041 5 | 6 | 7 | def json_tag_example(): 8 | conn = None 9 | try: 10 | conn = taosws.connect(host=host, port=port, user="root", password="taosdata") 11 | # create database 12 | rows_affected: int = conn.execute(f"CREATE DATABASE IF NOT EXISTS example_json_tag") 13 | print(f"Create database power successfully") 14 | assert rows_affected == 0 15 | 16 | rows_affected = conn.execute( 17 | "create table if not exists example_json_tag.stb (ts timestamp, v int) tags(jt json)" 18 | ) 19 | print(f"Create stable example_json_tag.stb successfully") 20 | assert rows_affected == 0 21 | 22 | conn.execute("use example_json_tag") 23 | 24 | stmt = conn.statement() 25 | stmt.prepare("INSERT INTO ? using stb tags(?) VALUES (?,?)") 26 | stmt.set_tbname("tb1") 27 | stmt.set_tags([taosws.json_to_tag('{"name":"value"}')]) 28 | 29 | stmt.bind_param([taosws.millis_timestamps_to_column([1686844800000]), taosws.ints_to_column([1])]) 30 | 31 | stmt.add_batch() 32 | rows = stmt.execute() 33 | assert rows == 1 34 | stmt.close() 35 | 36 | result = conn.query("SELECT ts, v, jt FROM example_json_tag.stb limit 100") 37 | for row in result: 38 | print(f"ts: {row[0]}, v: {row[1]}, jt: {row[2]}") 39 | 40 | except Exception as err: 41 | print(f"Failed to execute json_tag_example, db addrr:{host}:{port} ; ErrMessage:{err}") 42 | finally: 43 | if conn: 44 | conn.close() 45 | 46 | 47 | def all_type_example(): 48 | conn = None 49 | try: 50 | conn = taosws.connect(host=host, port=port, user="root", password="taosdata") 51 | cursor = conn.cursor() 52 | # create database 53 | rows_affected: int = cursor.execute(f"CREATE DATABASE IF NOT EXISTS all_type_example") 54 | print(f"Create database power successfully") 55 | assert rows_affected == 0 56 | conn.execute("use all_type_example") 57 | 58 | cols = [ 59 | "ts timestamp", 60 | "int_col INT", 61 | "double_col DOUBLE", 62 | "bool_col BOOL", 63 | "binary_col BINARY(100)", 64 | "nchar_col NCHAR(100)", 65 | "varbinary_col VARBINARY(100)", 66 | "geometry_col GEOMETRY(100)", 67 | ] 68 | tags = ["int_tag INT", "double_tag DOUBLE", "bool_tag BOOL", "binary_tag BINARY(100)", "nchar_tag NCHAR(100)"] 69 | 70 | str_cols = ",".join(cols) 71 | str_tags = ",".join(tags) 72 | print(str_cols, str_tags) 73 | cursor.execute("create table IF NOT EXISTS all_type_example.stb(%s) tags(%s)" % (str_cols, str_tags)) 74 | print(f"Create stable all_type_example.stb successfully") 75 | 76 | varbinary = bytes([0x01, 0x02, 0x03, 0x04]) 77 | 78 | geometry = bytearray( 79 | [ 80 | 0x01, 81 | 0x01, 82 | 0x00, 83 | 0x00, 84 | 0x00, 85 | 0x00, 86 | 0x00, 87 | 0x00, 88 | 0x00, 89 | 0x00, 90 | 0x00, 91 | 0x59, 92 | 0x40, 93 | 0x00, 94 | 0x00, 95 | 0x00, 96 | 0x00, 97 | 0x00, 98 | 0x00, 99 | 0x59, 100 | 0x40, 101 | ] 102 | ) 103 | 104 | stmt = conn.statement() 105 | stmt.prepare("INSERT INTO ? using stb tags(?,?,?,?,?) VALUES (?,?,?,?,?,?,?,?)") 106 | stmt.set_tbname("tb1") 107 | 108 | stmt.set_tags( 109 | [ 110 | taosws.int_to_tag(1), 111 | taosws.double_to_tag(1.1), 112 | taosws.bool_to_tag(True), 113 | taosws.varchar_to_tag("hello"), 114 | taosws.nchar_to_tag("stmt"), 115 | ] 116 | ) 117 | 118 | stmt.bind_param( 119 | [ 120 | taosws.millis_timestamps_to_column([1686844800000]), 121 | taosws.ints_to_column([1]), 122 | taosws.doubles_to_column([1.1]), 123 | taosws.bools_to_column([True]), 124 | taosws.varchar_to_column(["hello"]), 125 | taosws.nchar_to_column(["stmt"]), 126 | taosws.varbinary_to_column([b"0x7661726332"]), 127 | taosws.geometry_to_column([geometry]), 128 | ] 129 | ) 130 | 131 | stmt.add_batch() 132 | rows = stmt.execute() 133 | assert rows == 1 134 | stmt.close() 135 | 136 | cursor.execute("SELECT * FROM all_type_example.stb limit 100") 137 | 138 | data_dict = cursor.fetchallintodict() 139 | print(data_dict) 140 | cursor.close() 141 | except Exception as err: 142 | print(f"Failed to create db and table, db addrr:{host}:{port} ; ErrMessage:{err}") 143 | finally: 144 | if conn: 145 | conn.close() 146 | 147 | 148 | if __name__ == "__main__": 149 | json_tag_example() 150 | all_type_example() 151 | --------------------------------------------------------------------------------