mirror of
https://github.com/taosdata/TDengine
synced 2026-05-24 10:09:01 +00:00
* feat: [TS-6100] Fix virtual supertable plan. * add case stream1 for vehicle * test(stream): photovoltaic scene #TD-36783 * fix: errlog * test: update test case. * test: update test case. * modify case (#32016) * fix: check node type * feat: [TS-6100] set proper curr clause when translating stream trigger. * test asan error * feat: [TD-36758] Fix MAX_DELAY wrong precision. * case: restore bug9 case * test asan error * test: add cases * test: add test for manual recalc. * add case * add case * test: event manual pass. * test: add case into ci * feat: [TD-36761] Remove COMMENT in stream tag def * test(stream): ci test replay #TD-36808 * test: make manual ok. * test: reproduce bugs * case: JIRA TD-36815 set ignore this check * JIRA TD-36815 ignore * feat: [TD-36786] collect now and today to timeRangeExpr * fix: stack overflow * feat: [TD-36709] Optimize create vtable and table's error msg * test: reproduce bugs * test: add bug 11. * add case * test: add cases * test: add vehicle bug1 * test: add cases * test: add cases (#32041) * test: cases * test(stream): photovoltaic scene testing #TD-36783 * test: add test for manual. * fix: force output * add case * add case * remove wrong path case * test(stream): pv scene #TD-36783 * case : add bug2 for vehicle * test: reproduce cases * test(stream): add case #TD-36808 * test: update test case. * test: move case to new test frame. * fix: output * enh: support indef rows functions in extwindow (#32047) * enh: add operator reset func * fix: merge join reset issue * fix: memory issues * fix: add debug assert * fix: memory issues * fix: memory leak * fix: memory issues * fix taos log miss * fix: case issue * fix: case issue * fix: case issues * fix: drop dnode issue * fix: memory issues * fix: memory issues * fix: memory leak issues * fix: recalculate time range issue * fix: add debug log * fix: memory issues * fix: enable case asan * Update streamlist_for_ci.task * fix: case asan issue * fix: stream name issue * fix: external window compile issues * fix: deploy memory issue * fix: ahandle issue * fix: ahandle issue * fix: ahandle issue * fix: virtual table reader list issue * fix: log info * fix: msg error * fix: virtual table addr list issue * fix: memory issues * fix: memory leak issue * fix: memory issues * fix: memory free issues * fix: memory issues * fix: snode deploy issue * fix: mnode reader issue * fix: memory issues * fix: add debug test * enh: add ignore nodata trigger * fix: memory leaks * fix: configuration issue * fix: memory issue * fix: external window issue * fix: external window issues * fix: external window placeholder issue * fix: placeholder function init issues * fix: memory leak issue * fix: add debug log * fix: compile issues * fix: double free issue * fix: runner addr update issue * fix: msg rsp issue * fix: external window reset issue * fix: configuration issue * fix: deploy msg issue * fix: compile issue * fix: external window idx issue * fix: ci issues * fix: ci case issues * fix: drop dnode issue * fix: add debug log * fix: conflict * fix: create stream if not exists issue * fix: ahandle memory leak * fix: case issue * fix: exchange issues * fix: crash issue * fix: exchange prefetch issue * fix: snode quit issue * enh: support indef rows func * fix: crash issues * Fix external window collect vector function * fix: external window indef rows issues --------- Co-authored-by: huohong <sallyhuo@taosdata.com> Co-authored-by: Jing Sima <simondominic9997@outlook.com> * fix: log * feat: [TD-36838] Fix trigger virtual super table * test: vehicle add bug3 * test: vehicle add bug3 pass * test: vehicle add bug3 2 * test: vehicle add bug3 write end * fix(stream): fix filter of virtual tables in trigger task * fix(stream): add virtual table pseudo column value request * fix(stream): get tag for virtual child table * fix(stream): fetch virtual table pseudo columns in trigger * fix(stream): get tag value for vtable * fix(stream): ignore trigger control msg at server close down * fix(stream): retry pull/calc request in trigger task * test(stream): modify and disable some cases * fix: external window issue (#32052) * enh: add operator reset func * fix: merge join reset issue * fix: memory issues * fix: add debug assert * fix: memory issues * fix: memory leak * fix: memory issues * fix taos log miss * fix: case issue * fix: case issue * fix: case issues * fix: drop dnode issue * fix: memory issues * fix: memory issues * fix: memory leak issues * fix: recalculate time range issue * fix: add debug log * fix: memory issues * fix: enable case asan * Update streamlist_for_ci.task * fix: case asan issue * fix: stream name issue * fix: external window compile issues * fix: deploy memory issue * fix: ahandle issue * fix: ahandle issue * fix: ahandle issue * fix: virtual table reader list issue * fix: log info * fix: msg error * fix: virtual table addr list issue * fix: memory issues * fix: memory leak issue * fix: memory issues * fix: memory free issues * fix: memory issues * fix: snode deploy issue * fix: mnode reader issue * fix: memory issues * fix: add debug test * enh: add ignore nodata trigger * fix: memory leaks * fix: configuration issue * fix: memory issue * fix: external window issue * fix: external window issues * fix: external window placeholder issue * fix: placeholder function init issues * fix: memory leak issue * fix: add debug log * fix: compile issues * fix: double free issue * fix: runner addr update issue * fix: msg rsp issue * fix: external window reset issue * fix: configuration issue * fix: deploy msg issue * fix: compile issue * fix: external window idx issue * fix: ci issues * fix: ci case issues * fix: drop dnode issue * fix: add debug log * fix: conflict * fix: create stream if not exists issue * fix: ahandle memory leak * fix: case issue * fix: exchange issues * fix: crash issue * fix: exchange prefetch issue * fix: snode quit issue * enh: support indef rows func * fix: crash issues * Fix external window collect vector function * fix: external window indef rows issues * fix: external window issue --------- Co-authored-by: huohong <sallyhuo@taosdata.com> Co-authored-by: Jing Sima <simondominic9997@outlook.com> * test: add nevados cases * test(stream): fix tobacco case of stream 11 #TD-36514 * test(stream): photovoltaic scene #TD-36783 * fix: output for proj * test:remove nevados.py * add stream case * fix(stream): fix interval window open with watermark * test: add old cases * add case * test:add old case * fix: cann't merge externalwindows * fix(stream): batch get block when fetch data in stream reader * test: add cases to ci (#32064) * test: update test case. * test: update test case. * case: add case * test:add old cases * fix(stream): batch get block when fetch data in stream reader * test: add case into ci * test: add case into ci * fix: datainserter no data * fix: stream result row data inconsistency error (#32053) * fix: set null type * feat: [TS-6100] Fix compile error * feat: [TD-36866] Fix state window expr * test: add bug4 for vehicle * case: bug4 add null update * test(stream): migrate old py cases #TD-36887 * fix(stream): batch get block when fetch data in stream reader * test: compatibility ok. * fix(stream): batch get block when fetch data in stream reader * test: add more versions for stream compatibility. * test: test_empty_identifier.py ok. * fix(stream): columnInfo is null if pData==NULL * test(stream): migrate old py case #TD-36887 * test(stream): migrate old case #TD-36887 * test: add case stream4 ok * test: add case into ci * test: add old cases into ci * test: add old cases into ci * add case * modify case * test: migrate empty_identifier.py to new test framework. * test: add bug5 for vehicle * test: remove mistakenly added files * case: bug5 run failed modify * add stream common * add stream case * add test case * test: bug5 add self.step variant * test: add cases (#32094) * test: migrate test_drop.py. * test: update test case. * add case * add case * test: add use case * Feat/ts 6100 3.0.0722m (#32103) * migrate system-test/2-query * revert file * update case.task * resolve script migrate * run new test framework on new_testcases * migrate system-test/2-query * format docstring * fix test validation * fix test validation * fix error * migrate army case * migrate army case * fix error * migrate system-test/2-query * migrate system-test/2-query * migrate system-test/2-query * migrate system-test/2-query * test exe time * fix ci error * migrate system-test/1-insert * new common function * migrate system-test/1-insert * fix ci error * migrate system-test/1-insert * feat: add configuration and script for memory allocator settings * fix: correct HEAPPROFILE path and remove redundant metadata_thp setting in memory allocator script * fix ci error * migrate system-test/1-insert, 2-query * feat:insert into subquery (#31401) (#31710) * migrate system-test/99-TDcase * feat(gpt): add grant check for gpt. (#31708) * migrate system-test/99-TDcase * migrate system-test/99-TDcase * migrate system-test/99-TDcase * fix/send-heartbeat-statis (#31680) * migrate system-test/7-tmq * chore: support cmake option TAOSWS_GIT_TAG like taosadapter [skip ci] (#31486) * add system-test/6/cluster test * chore: move default branch from main to 3.3.6 for adapter/taosws * fix docstring validation * migrate system-test/7-tmq * migrate system-test/7-tmq * feat: add set_taos_malloc_env configuration and update related scripts * Update 03-kubernetes.md * enh: add log for snapshot (#31681) * simple test * more * migrate system-test/7-tmq * migrate system-test 0-others cases * migrate system-test/7-tmq * fix docstring validation * migrate system-test/7-tmq * fix docstring validation * fix: invalid queue * fix docstring validation * refactor: reorganize memory allocator script constants and improve mode descriptions * enh: TD-36324-improve-sync-heartbeat-log (#31727) * recover log level * fix: taosd crush in query when insufficient memory (#31746) Co-authored-by: Tony Zhang <tonyzhang@taosdata.com> * fix: overflow check in snprintf (#31780) * migrate system-test/7-tmq * migrate some cases * migrate system-test/7-tmq * fix failed cases * fix failed cases * migrate some cases * fix failed cases * fix failed cases * migrate testcases * fix: update environment file path in taosd.service and adjust set_taos_malloc.sh configuration * fix: set lcn before do s3 migrate. (#31782) * migrate testcases * fix failed cases * fix: add condition to set default malloc config for taosd service * fix: update usage message in set_taos_malloc.sh to include quiet mode option * fix: add quiet mode option to set malloc config in install script * fix: update set_taos_malloc.sh to improve output messages and adjust default malloc config invocation * recover log level * fix(tmq): [TS-6569]tdb error if write tmq meta data in multi thread (#31808) * fix: the calculation of dnode uptime (#31832) * migrate testcase * fix: correct timediff function bug and redress docs (#31798) * fix: enhance malloc configuration for taosd and taosadapter in install script * fix: TD-36442 show full condition (#31796) * enh/TD-36466-sync-heartbeat (#31805) * fix(plan) virtual table support BI moudle when use in select (#31787) * fix failed cases * enh: TS-5926-force-repair-wal (#31828) * migrate testcase * rename taos & taosd (#31855) * migrate testcase * migrate testcase * fix failed cases * fix: update environment file paths in taosd.service and set_taos_malloc.sh * feat: add performance tuning documentation for memory optimization and set_taos_malloc.sh usage * fix: update file paths in performance tuning documentation for consistency * feat: add performance tuning documentation for memory allocator configuration script * fix: add missing line breaks for improved readability in performance tuning documentation * fix: adjust heading levels for consistency in performance tuning documentation * fix cases * fix new_testcases * fix failed cases * fix(query): support show tags on virtual table (#31831) * fix failed cases * docs: replace mysql screenshot (#31888) * feat: use the new TDengine product name (#31859) * merge 3.0 * fix failed cases * fix failed cases * merge 3.0 * fix cases * fix cases * doc: Update 03-stream.md (#31675) * chore(deps): bump requests from 2.27.1 to 2.32.4 in /test (#31326) Bumps [requests](https://github.com/psf/requests) from 2.27.1 to 2.32.4. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.27.1...v2.32.4) --- updated-dependencies: - dependency-name: requests dependency-version: 2.32.4 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix new cases * fix:Convert line endings from LF to CRLF for ans file * build(deps): bump golang.org/x/net in /tools/keeper (#30811) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.36.0 to 0.38.0. - [Commits](https://github.com/golang/net/compare/v0.36.0...v0.38.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-version: 0.38.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump urllib3 from 1.26.20 to 2.5.0 in /test (#31414) Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.20 to 2.5.0. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/1.26.20...2.5.0) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.5.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump org.apache.tomcat.embed:tomcat-embed-core (#31392) Bumps org.apache.tomcat.embed:tomcat-embed-core from 9.0.104 to 9.0.106. --- updated-dependencies: - dependency-name: org.apache.tomcat.embed:tomcat-embed-core dependency-version: 9.0.106 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump org.apache.tomcat.embed:tomcat-embed-core (#31393) Bumps org.apache.tomcat.embed:tomcat-embed-core from 9.0.104 to 9.0.106. --- updated-dependencies: - dependency-name: org.apache.tomcat.embed:tomcat-embed-core dependency-version: 9.0.106 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump org.apache.kafka:kafka-clients (#31341) Bumps org.apache.kafka:kafka-clients from 3.9.0 to 3.9.1. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-version: 3.9.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: add lock when calculating log buffer start/end (#31883) * fix new cases * fix new cases * fix failed cases * feat: new stream (#31678) * fix: windows compile issue * test: add vtable cases (#31829) * fix: windows compile issues * test:add test cases * fix: windows compile issue * case: em-4 stream case submit * test: stream4_sub1 found bug2 * test: submit test_scene_meters_bug2.py * add stream parameters example * feat: [TS-6100] Do not translate const value as column. * Feat/ts 6100 3.0 zlv (#31747) * modify asan exampel * modify asan exampel * add example * add example * modify case example --------- Co-authored-by: zelv01 <1101510017@qq.com> * feat(stream): fix memory leak * modify sliding example * test: update test case. * feat(stream): fix conflicts * fix: add offset case 10a 10s 10m 10h 10d * feat(stream): fix conflicts * chore(stream): rename case name #TS-6100 * add case * modify example * fix: windows compile issues * fix: data null check * feat: [TS-6100] Forbid where when using %%trows (#31827) * feat: [TS-6100] Forbid where when using %%trows * test: update cases * feat: [TS-6100] Fix leaks. --------- Co-authored-by: Simon Guan <guanshengliang@qq.com> * test: reproduce bugs * test: update test case. * test: update test case. * feat: [TS-6100] Fix leaks. * test: add cases * Feat/ts 6100 3.0.pw10 (#31841) * enh: add operator reset func * fix: merge join reset issue * fix: memory issues * fix: add debug assert * fix: memory issues * fix: memory leak * fix: memory issues * fix taos log miss * fix: case issue * fix: case issue * fix: case issues * fix: drop dnode issue * fix: memory issues * fix: memory issues * fix: memory leak issues * fix: recalculate time range issue * fix: add debug log * fix: memory issues * fix: enable case asan * Update streamlist_for_ci.task * fix: case asan issue * fix: stream name issue * fix: external window compile issues * fix: deploy memory issue * fix: ahandle issue * fix: ahandle issue * fix: ahandle issue * fix: virtual table reader list issue * fix: log info * fix: msg error * fix: virtual table addr list issue * fix: memory issues * fix: memory leak issue * fix: memory issues * fix: memory free issues * fix: memory issues * fix: snode deploy issue * fix: mnode reader issue * fix: memory issues * fix: add debug test * enh: add ignore nodata trigger * fix: memory leaks * fix: configuration issue * fix: memory issue * fix: external window issue * fix: external window issues * fix: external window placeholder issue * fix: placeholder function init issues * fix: memory leak issue * fix: add debug log * fix: compile issues * fix: double free issue * fix: runner addr update issue * fix: msg rsp issue * fix: external window reset issue * fix: configuration issue * fix: deploy msg issue * fix: compile issue --------- Co-authored-by: huohong <sallyhuo@taosdata.com> * test: reproduce bugs * fix: add sliding interval combine case * test: add cases * test: add recalc test. * test: reproduce bugs * case : add vt ts is null check * modify case * bug: submit test_idmp_meters_bug3.py * test: add test for recalc. * test: add cases * fix: error code check * test: add cases * fix(stream): scan wal with schema in that version * add case * test: add cases * test: update test case. * fix: windows compile issues * add case * test: add cases (#31845) * modify case * fix: reset interpPrev * test: add test_idmp_meters bug4 and bug3 * add case * fix(stream): opti wal interface * fix: remove test_idmp_meters_bug5.py * test: add cases * fix(stream): fix ts data fetch for virtual tables * cancel asan case * test: update test case. * test: update test case. * add case * test: add cases * test: add cases * test: add case test_idmp_meters_bug5.py * test: update test case. * fix(stream): tmq error * test: add cases * feat: [TS-6100] Restore deleted code in mndSma.c since they are still in use. * fix(stream): optimize val scan logic * test: add test_recalc_expired_time.py to ci. * test: update test case. * test: update test case. * feat: [TS-6100] Fix fill range check * fix(stream): optimize val scan logic * add case * test: modify for partition by %%1 * test: add fun case stream4_sub7 * fix(stream): optimize val scan logic * add case * feat: [TS-6100] Rename OPTIONS to STREAM_OPTIONS. * test: add test for recalc. * test: use stream_options. * fix: some cases error. * test: remove recalc from ci. * fix: ci case issues (#31880) * enh: add operator reset func * fix: merge join reset issue * fix: memory issues * fix: add debug assert * fix: memory issues * fix: memory leak * fix: memory issues * fix taos log miss * fix: case issue * fix: case issue * fix: case issues * fix: drop dnode issue * fix: memory issues * fix: memory issues * fix: memory leak issues * fix: recalculate time range issue * fix: add debug log * fix: memory issues * fix: enable case asan * Update streamlist_for_ci.task * fix: case asan issue * fix: stream name issue * fix: external window compile issues * fix: deploy memory issue * fix: ahandle issue * fix: ahandle issue * fix: ahandle issue * fix: virtual table reader list issue * fix: log info * fix: msg error * fix: virtual table addr list issue * fix: memory issues * fix: memory leak issue * fix: memory issues * fix: memory free issues * fix: memory issues * fix: snode deploy issue * fix: mnode reader issue * fix: memory issues * fix: add debug test * enh: add ignore nodata trigger * fix: memory leaks * fix: configuration issue * fix: memory issue * fix: external window issue * fix: external window issues * fix: external window placeholder issue * fix: placeholder function init issues * fix: memory leak issue * fix: add debug log * fix: compile issues * fix: double free issue * fix: runner addr update issue * fix: msg rsp issue * fix: external window reset issue * fix: configuration issue * fix: deploy msg issue * fix: compile issue * fix: external window idx issue * fix: ci issues --------- Co-authored-by: huohong <sallyhuo@taosdata.com> * fix(stream): fix compilation error * fix(stream): optimize val scan logic * test:add test cases * test: modify case * fix: external agg error * test(stream): tobacco scene testing #TD-36514 * test: add stream cases (#31885) * fix: windows compile issue * fix: calc timerange * fix: windows compile issue * modify case * fix(stream): compile error * test: remove one debug test case file * test: modify * test: add test cases * test: reproduce bugs * test: reproduce bugs * feat: [TS-6100] Placeholder function should only appera in SELECT and… (#31868) * feat: [TS-6100] Placeholder function should only appera in SELECT and WHERE and FROM. * test: update case --------- Co-authored-by: Simon Guan <guanshengliang@qq.com> * add example * add example * modify case example * modify case * test:alter sql * test: add stream5 case * fix(stream): get schema error with version * test: add delete recalc test py. * test: remove bug cases * test: stream5 case test passed * test: add state cases (#31893) * fix(stream): compile error * test: modify case * test: add cases * test: add test. * test: update test case. * chore(test): fix case err * test: update test case. * fix: align data get * fix(stream): fix row index of datablock written into data cache * fix: put align data * test: update test case. * test: add test cases for virtual table * chore(test): fix case err #TD-36514 * add case * test: add test for water mark. * test: add meters bug6 for stream5 * test: add cases (#31903) * test: add test for recalc. * feat: [TS-6100] %%trows can only be used when event type is window close. * test: add precision of database for ms/us/ns * modify case * add case * add case * test: add test to ci. * modify case * fix: ci case issues (#31904) * enh: add operator reset func * fix: merge join reset issue * fix: memory issues * fix: add debug assert * fix: memory issues * fix: memory leak * fix: memory issues * fix taos log miss * fix: case issue * fix: case issue * fix: case issues * fix: drop dnode issue * fix: memory issues * fix: memory issues * fix: memory leak issues * fix: recalculate time range issue * fix: add debug log * fix: memory issues * fix: enable case asan * Update streamlist_for_ci.task * fix: case asan issue * fix: stream name issue * fix: external window compile issues * fix: deploy memory issue * fix: ahandle issue * fix: ahandle issue * fix: ahandle issue * fix: virtual table reader list issue * fix: log info * fix: msg error * fix: virtual table addr list issue * fix: memory issues * fix: memory leak issue * fix: memory issues * fix: memory free issues * fix: memory issues * fix: snode deploy issue * fix: mnode reader issue * fix: memory issues * fix: add debug test * enh: add ignore nodata trigger * fix: memory leaks * fix: configuration issue * fix: memory issue * fix: external window issue * fix: external window issues * fix: external window placeholder issue * fix: placeholder function init issues * fix: memory leak issue * fix: add debug log * fix: compile issues * fix: double free issue * fix: runner addr update issue * fix: msg rsp issue * fix: external window reset issue * fix: configuration issue * fix: deploy msg issue * fix: compile issue * fix: external window idx issue * fix: ci issues * fix: ci case issues * fix: drop dnode issue --------- Co-authored-by: huohong <sallyhuo@taosdata.com> * fix(stream): ci error * test: update test case. * feat: [TS-6100] Disable some failed UT. * feat: [TS-6100] Fix virtual table * test: add bug 5. * test: add test delete recalc to ci. * test: add bug 6. * test(stream): tobacco scene #TD-36514 * fix: reqCids,reqCols memory leak in SSTriggerRealtimeContext Co-authored-by: Tony Zhang <tonyzhang@taosdata.com> * test: add case stream6 * fix(stream): implement some pending features in trigger task * modify case * modify case * fix: case issues * modify case * test: add recalc for warter mark. * fix(stream): fix count window trigger of virtual tables * fix(stream): memory leak * test: fix run err. * test: add stream6 bug7 * fix: adjust format * test(stream): tobacco scene testing #TD-36514 * test: change bug7 with update window1 and 2 * test: add test bug 7. * case: restore write 3 window * fix: windows compile issue * fix: notify * test: add cases * modify case * test: update test case. * test(stream): toobacco scene testing #TD-36514 --------- Co-authored-by: Simon Guan <slguan@taosdata.com> Co-authored-by: plum-lihui <huili@taosdata.com> Co-authored-by: Alex Duan <417921451@qq.com> Co-authored-by: zelv01 <1101510017@qq.com> Co-authored-by: Jing Sima <simondominic9997@outlook.com> Co-authored-by: xiangyang guo <66111494+happyguoxy@users.noreply.github.com> Co-authored-by: wangmm0220 <wangmm0220@gmail.com> Co-authored-by: Haojun Liao <hjliao@taosdata.com> Co-authored-by: zyyang90 <zyyang@taosdata.com> Co-authored-by: Alex Duan <51781608+DuanKuanJun@users.noreply.github.com> Co-authored-by: facetosea <285808407@qq.com> Co-authored-by: Simon Guan <guanshengliang@qq.com> Co-authored-by: huohong <sallyhuo@taosdata.com> Co-authored-by: Li Hui <52318143+plum-lihui@users.noreply.github.com> Co-authored-by: Jinqing Kuang <kuangjinqingcn@gmail.com> Co-authored-by: xiao-77 <berylbao@taosdata.com> Co-authored-by: Zhixiao Bao <62235797+xiao-77@users.noreply.github.com> Co-authored-by: happyguoxy <happy_guoxy@163.com> Co-authored-by: Tony Zhang <34825804+Tony2h@users.noreply.github.com> Co-authored-by: Tony Zhang <tonyzhang@taosdata.com> * test: rename TSDB * docs: fix rust examples (#31908) * docs: modify rust native test case * docs: modify rust ws test case * docs: modify rust examples * docs: update rust pool docs * fix new cases * migrate test case * feat: support reading sub table names and tag values from CSV files to create sub tables (#31909) * feat: add obtaining table names from tag files * feat: add write data table control * feat: add table params to write * feat: delete log file * feat: modify test case csv path * feat: resolve memory leakage in the table building thread * feat: resolve compilation errors * feat: resolve table name copy len error * feat: modify create table log level * feat: modifying query configuration parameter array out of bounds * feat: support custom primary key names * feat: modify log level * feat: add set primary key name case * feat: add column keywords case * feat: add keywords case data * feat: modify primaryKeyName value len * feat: modify primaryKeyName value define * feat: modify primaryKeyName value size * fix: compile issue (#31943) Co-authored-by: taos-support <it@taosdata.com> * package: fix error * package: fix error * fix failed cases * merge 3.0 * rename create_table_keywords.py to test_create_table_keywords.py * fix failed cases * fix new cases * docs: update stream (#31957) * docs: update jdbc out-dated descripiton (#31959) * fix: TD-36560 refactor arbitrator group function name and log (#31852) * feat: support BLOB data type (#31704) * rename 0-others/mounts.py to 0-others/test_mounts.py * fix failed cases * docs: update gpt (#31975) * fix failed cases * fix failed cases * package: fix error * feat: add taosBenchmark command line parameters (#31967) * feat: add command line parameters * feat: add command line parameter test cases * fix: tableName len error * enh: set TD Release build in tdengine-build.yml * Update tdengine-build.yml * fix: add json file path log * fix: streamline TD_CONFIG export in build steps * fix: Restore the build configuration --------- Co-authored-by: haoranchen <haoran920c@163.com> * fix: tableName len error (#31977) * fix: tableName len error * fix: modify TD_CONFIG=Release * fix: code format * fix: Restore the build configuration * enh: set TD Release build in tdengine-build.yml (#31980) * enh: set TD Release build in tdengine-build.yml * Update tdengine-build.yml * fix: update cache key for externals to include debug build version * fix: remove verbose flag from build commands in tdengine-build.yml * skip memleak cases * fix failed case * fix failed cases * package: fix error * fix(stmt2):tbname error output (#31997) * fix: possible memory leak (#31972) * feat: create connect add dbname params (#32002) * feat: create connect add dbname params * fix: connect param error * skip failed cases * fix cases on windows * fix cases * support connect bi mode and fix log level * unique sql connect username and password * fix log level * enh: mounted vnode may have no tq (#31916) * fix: subquery memleak (#32024) * fix failed case * fix cases * rename 2-query/test_insert_select.py to 2-query/test_system_insert_select.py * skip memleak cases * enh: rename data forecast/detect to forecasting/anomaly detection (#32021) * package: unique product name * package: update for main * skip tsim cases * chore: update jdbc connection pool validation query sql (#32056) * refactor: quotes usage in bash scripts Signed-off-by: WANG Xu <feici02@outlook.com> * enhn(mqtt/rawblock): new format for msg payload (#31801) * fix: fix broken link in 14-stream.md * docs: 15-spark.md is missing end semicolon (#32068) * chore: bump dev version to 3.3.7.0.alpha (#32066) * fix: blob test (#32020) * fix blob query error * fix blob query error * fix blob query error * fix blob query error * fix blob query error * opt query * opt write * opt write * opt write * opt bse * opt write * opt write * opt write * opt write * opt write * opt write * opt write * opt write * opt write * opt write * opt write * opt write * opt write * add cache * opt query * opt query * opt bse * add data iter * add data iter * add more compress * add more compress * add more compress * add more compress * add more compress * add more compress * opt blob transfer * opt blob transfer * opt blob transfer * opt write * avoid unordered data write * avoid unordered data write * opt read * refactor log * fix invalid write * refactor code * fix merge error * fix merge error * add error code * support blob type len * support blob type len * support blob type len * support blob type len * support blob type len * support blob type len * support blob type len * support blob type len * support blob type len * support blob type len * Merge remote-tracking branch 'origin/3.0' into enh/blob * refactor code * support blob type len * refactor code * refactor code * benchmark support blob type * benchmark support blob type * add log * handle sort and merge row * change file set * change file set * change file set * change file set * change file set * change file set * change file set * opt code * opt read * add restart error and add unit test * add restart error and add unit test * add restart error and add unit test * add restart error and add unit test * add restart error and add unit test * refactor code * add restart error and add unit test * opt code * refactor code * fix invalid write * blob test * blob test * blob test * blob test * blob test * support blob * add str trim * add str trim * Merge remote-tracking branch 'origin/3.0' into feat/blob * Merge remote-tracking branch 'origin/3.0' into feat/blob * update test case * fix invalid read * fix invalid read * fix invalid read * add stmt2 * add stmt2 * add stmt2 * update parameter * refactor test case * refactor test case * support blob * support stmt2 * add sub * support blob * support blob * support sub * fix tmq crash * support windows/darwin * fix stmt2 bind row * fix blob crash * fix blob crash * fix blob query error * fix blob crash * fix merge error * refactor bse * add blob transfer * add blob transfer * add transfer snapshot * refactor code * change log level * change log level * add test case * refactor code * revert taosBenchmark * revert taosbench * fix: improve error handling and encoding for file reading in grep_asserts_in_file function * rm assert * fix mem leak * fix compile error * fix conflict * fix conflict * fix compile error * fix compile error * fix pre check error * fix pre check error * fix compile error on windows * fix error on window * fix error on windows * fix error on windows * opt no-blob sql * fix compile error on dawain * fix compile error on dawain * fix invalid read * fix mem leak * fix invalid read * fix invalid read * fix error on windows * remove unused code * refactor code * fix mem leak * fix mem leak * rm unused code * rm unused code * fix invalid copy * fix invalid copy * fix invalid copy * fix invalid copy * fix invalid copy * refactor code * make ci happy * refactor code * change make * update test case * update ignore code * update bse snapshot and test case * update bse snapshot and test case * refactor code * change unit test * update bse snapshot * update bse snapshot * fix test case * fix bse snapshot * fix bse snapshot * fix snapshot transfer * remove unused log * support func query * add test case * forbidden unsupport code * merge 3.0 * merge 3.0 * change test case * add forbidden code * add forbidden code * add code * support length func * support length func * taosBenchmark support blob * support blob raw block * support write raw block * support more query * Merge branch 'feat/blob' into feat/blob_test * SBlobRow2 * change bse commit change * refactor code * rm exe test * refactor code * checke return code * rename blob name * refactor code * refactor code * refactor code * refactor code * fix unordere write * fix unordere write * fix row merge error * fix row merge error * fix row merge error * fix row merge error * support ordered data * support ordered data * Merge remote-tracking branch 'origin/3.0' into feat/blob_test * fix stmt2 blob crash * add not support write type * add not support write type * add not support write type * rm exe * fix col-formate error * fix col-formate error * fix mem leak * refactor code * add error code for single blob column restriction and update error message * add error code for single blob column restriction and update error message * add error code for single blob column restriction and update error message * refactor code * change error code * make ci happy --------- Co-authored-by: yihaoDeng <yhdeng@taosdata.com> Co-authored-by: chenhaoran <haoran920c@163.com> * package: update for main (#32091) * fix compile error on windows (#32089) * fix: source code merge issues --------- Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: WANG Xu <feici02@outlook.com> Co-authored-by: minhuinie <nminhui@163.com> Co-authored-by: huohong <sallyhuo@taosdata.com> Co-authored-by: chenhaoran <haoran920c@163.com> Co-authored-by: Nie Minhui <143420805+minhuinie@users.noreply.github.com> Co-authored-by: Mario Peng <48949600+Pengrongkun@users.noreply.github.com> Co-authored-by: Haojun Liao <hjxilinx@users.noreply.github.com> Co-authored-by: dongming chen <cademfly@hotmail.com> Co-authored-by: Linhe Huo <linhehuo@gmail.com> Co-authored-by: huohong <346479823@qq.com> Co-authored-by: Joel Brass <joel@jbrass.com> Co-authored-by: WANG Xu <feici02@outlook.com> Co-authored-by: Hongze Cheng <hzcheng@taosdata.com> Co-authored-by: Tony Zhang <34825804+Tony2h@users.noreply.github.com> Co-authored-by: Tony Zhang <tonyzhang@taosdata.com> Co-authored-by: Kaili Xu <klxu@taosdata.com> Co-authored-by: Zhixiao Bao <62235797+xiao-77@users.noreply.github.com> Co-authored-by: WANG MINGMING <wangmm0220@gmail.com> Co-authored-by: hongzhenliu <wluckyjob@gmail.com> Co-authored-by: Daniel Clow <106956386+danielclow@users.noreply.github.com> Co-authored-by: She Yanjie <57549981+sheyanjie-qq@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Simon Guan <slguan@taosdata.com> Co-authored-by: plum-lihui <huili@taosdata.com> Co-authored-by: Alex Duan <417921451@qq.com> Co-authored-by: zelv01 <1101510017@qq.com> Co-authored-by: Jing Sima <simondominic9997@outlook.com> Co-authored-by: xiangyang guo <66111494+happyguoxy@users.noreply.github.com> Co-authored-by: Haojun Liao <hjliao@taosdata.com> Co-authored-by: zyyang90 <zyyang@taosdata.com> Co-authored-by: Alex Duan <51781608+DuanKuanJun@users.noreply.github.com> Co-authored-by: facetosea <285808407@qq.com> Co-authored-by: Simon Guan <guanshengliang@qq.com> Co-authored-by: Li Hui <52318143+plum-lihui@users.noreply.github.com> Co-authored-by: Jinqing Kuang <kuangjinqingcn@gmail.com> Co-authored-by: xiao-77 <berylbao@taosdata.com> Co-authored-by: happyguoxy <happy_guoxy@163.com> Co-authored-by: guozhenwei <2227465945@qq.com> Co-authored-by: kevin men <men_shi_bin@163.com> Co-authored-by: taos-support <it@taosdata.com> Co-authored-by: Yihao Deng <luomoxyz@126.com> Co-authored-by: Minglei Jin <49711132+stephenkgu@users.noreply.github.com> Co-authored-by: yihaoDeng <yhdeng@taosdata.com> * test: add cases (#32102) * test: add old cases (#32101) * test: modify old case * add case * test: remove ts-5617. * test: add case bug6 for vehicle * add case * add case * test: modify old case * fix: merge issue * test: migrate test_interval.py. * fix: row merge strategy * test(stream): photovoltaic scene add ci #TD-36783 * test(stream): fix case #TD-36783 * test: add old cases (#32110) * test: update test case. * test: add use case * test: update test case. * test: update test case. * test: update test case. * fix: windows compile issue * test: modify for format * test(stream): fix ci warning #TD-36783 * test(stream): fix #TD-36514 * fix(stream): pass processOneBlock=true to vnodeProcessStreamFetchMsg when fetch data from virtual table to avoid heap use after free * fix: output tbname contains a dot * test(stream): fix ci warning #TD-36514 * test(stream): fix ci warning #TD-36887 * test: modify notes format * add case * test(stream): fix ci #TD-36514 * enh: support count like functions in ext window (#32082) * enh: add operator reset func * fix: merge join reset issue * fix: memory issues * fix: add debug assert * fix: memory issues * fix: memory leak * fix: memory issues * fix taos log miss * fix: case issue * fix: case issue * fix: case issues * fix: drop dnode issue * fix: memory issues * fix: memory issues * fix: memory leak issues * fix: recalculate time range issue * fix: add debug log * fix: memory issues * fix: enable case asan * Update streamlist_for_ci.task * fix: case asan issue * fix: stream name issue * fix: external window compile issues * fix: deploy memory issue * fix: ahandle issue * fix: ahandle issue * fix: ahandle issue * fix: virtual table reader list issue * fix: log info * fix: msg error * fix: virtual table addr list issue * fix: memory issues * fix: memory leak issue * fix: memory issues * fix: memory free issues * fix: memory issues * fix: snode deploy issue * fix: mnode reader issue * fix: memory issues * fix: add debug test * enh: add ignore nodata trigger * fix: memory leaks * fix: configuration issue * fix: memory issue * fix: external window issue * fix: external window issues * fix: external window placeholder issue * fix: placeholder function init issues * fix: memory leak issue * fix: add debug log * fix: compile issues * fix: double free issue * fix: runner addr update issue * fix: msg rsp issue * fix: external window reset issue * fix: configuration issue * fix: deploy msg issue * fix: compile issue * fix: external window idx issue * fix: ci issues * fix: ci case issues * fix: drop dnode issue * fix: add debug log * fix: conflict * fix: create stream if not exists issue * fix: ahandle memory leak * fix: case issue * fix: exchange issues * fix: crash issue * fix: exchange prefetch issue * fix: snode quit issue * enh: support indef rows func * fix: crash issues * Fix external window collect vector function * fix: external window indef rows issues * fix: external window issue * enh: support count always return value in external window * fix: force output when has more result block * fix: runner block retrieve issue * fix: crash issue * fix: count cases issue --------- Co-authored-by: huohong <sallyhuo@taosdata.com> Co-authored-by: Jing Sima <simondominic9997@outlook.com> Co-authored-by: facetosea <285808407@qq.com> * test(stream): fix ci warning #TD-36514 * modify example * test: update test case. * add case to ci (#32113) * add case to ci * modify example * fix: compile issues * test: add more test for stream. * feat: [TD-36624] Fix join table extract time range expr. * fix(stream): add missing fields in stream notification * test: add stream cases (#32129) * fix: error code check * feat: [TD-36624] Fix join table extract time range expr. * fix(stream): add log * modify example * feat: [TS-6100] Modify alter vtable test case * test: migrate test compatibility. * fix: ci check. * feat: [TS-6100] Fix leak * modify example * enh: external window support scalar (#32139) * enh: add operator reset func * fix: merge join reset issue * fix: memory issues * fix: add debug assert * fix: memory issues * fix: memory leak * fix: memory issues * fix taos log miss * fix: case issue * fix: case issue * fix: case issues * fix: drop dnode issue * fix: memory issues * fix: memory issues * fix: memory leak issues * fix: recalculate time range issue * fix: add debug log * fix: memory issues * fix: enable case asan * Update streamlist_for_ci.task * fix: case asan issue * fix: stream name issue * fix: external window compile issues * fix: deploy memory issue * fix: ahandle issue * fix: ahandle issue * fix: ahandle issue * fix: virtual table reader list issue * fix: log info * fix: msg error * fix: virtual table addr list issue * fix: memory issues * fix: memory leak issue * fix: memory issues * fix: memory free issues * fix: memory issues * fix: snode deploy issue * fix: mnode reader issue * fix: memory issues * fix: add debug test * enh: add ignore nodata trigger * fix: memory leaks * fix: configuration issue * fix: memory issue * fix: external window issue * fix: external window issues * fix: external window placeholder issue * fix: placeholder function init issues * fix: memory leak issue * fix: add debug log * fix: compile issues * fix: double free issue * fix: runner addr update issue * fix: msg rsp issue * fix: external window reset issue * fix: configuration issue * fix: deploy msg issue * fix: compile issue * fix: external window idx issue * fix: ci issues * fix: ci case issues * fix: drop dnode issue * fix: add debug log * fix: conflict * fix: create stream if not exists issue * fix: ahandle memory leak * fix: case issue * fix: exchange issues * fix: crash issue * fix: exchange prefetch issue * fix: snode quit issue * enh: support indef rows func * fix: crash issues * Fix external window collect vector function * fix: external window indef rows issues * fix: external window issue * enh: support count always return value in external window * fix: force output when has more result block * fix: runner block retrieve issue * fix: crash issue * fix: count cases issue * fix: reader deploy message issue * fix: task deploy issue * fix: external window scalar issue * fix: compile issue --------- Co-authored-by: huohong <sallyhuo@taosdata.com> Co-authored-by: Jing Sima <simondominic9997@outlook.com> Co-authored-by: facetosea <285808407@qq.com> * feat: [TS-6100] Fix show create table/vtable * fix: tagsName --------- Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: WANG Xu <feici02@outlook.com> Co-authored-by: Jing Sima <simondominic9997@outlook.com> Co-authored-by: Alex Duan <417921451@qq.com> Co-authored-by: zyyang90 <zyyang@taosdata.com> Co-authored-by: Zhixiao Bao <62235797+xiao-77@users.noreply.github.com> Co-authored-by: facetosea <285808407@qq.com> Co-authored-by: Li Hui <52318143+plum-lihui@users.noreply.github.com> Co-authored-by: Haojun Liao <hjliao@taosdata.com> Co-authored-by: Lv Ze <1101510017@qq.com> Co-authored-by: root <happy_guoxy@163.com> Co-authored-by: plum-lihui <huili@taosdata.com> Co-authored-by: xiao-77 <berylbao@taosdata.com> Co-authored-by: xiangyang guo <66111494+happyguoxy@users.noreply.github.com> Co-authored-by: Simon Guan <guanshengliang@qq.com> Co-authored-by: Alex Duan <51781608+DuanKuanJun@users.noreply.github.com> Co-authored-by: Simon Guan <slguan@taosdata.com> Co-authored-by: huohong <sallyhuo@taosdata.com> Co-authored-by: Zhiyu Yang <69311263+zyyang90@users.noreply.github.com> Co-authored-by: Jinqing Kuang <kuangjinqingcn@gmail.com> Co-authored-by: wangmm0220 <wangmm0220@gmail.com> Co-authored-by: Tony Zhang <34825804+Tony2h@users.noreply.github.com> Co-authored-by: minhuinie <nminhui@163.com> Co-authored-by: chenhaoran <haoran920c@163.com> Co-authored-by: Nie Minhui <143420805+minhuinie@users.noreply.github.com> Co-authored-by: Mario Peng <48949600+Pengrongkun@users.noreply.github.com> Co-authored-by: Haojun Liao <hjxilinx@users.noreply.github.com> Co-authored-by: dongming chen <cademfly@hotmail.com> Co-authored-by: Linhe Huo <linhehuo@gmail.com> Co-authored-by: huohong <346479823@qq.com> Co-authored-by: Joel Brass <joel@jbrass.com> Co-authored-by: WANG Xu <feici02@outlook.com> Co-authored-by: Hongze Cheng <hzcheng@taosdata.com> Co-authored-by: Tony Zhang <tonyzhang@taosdata.com> Co-authored-by: Kaili Xu <klxu@taosdata.com> Co-authored-by: hongzhenliu <wluckyjob@gmail.com> Co-authored-by: Daniel Clow <106956386+danielclow@users.noreply.github.com> Co-authored-by: She Yanjie <57549981+sheyanjie-qq@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: guozhenwei <2227465945@qq.com> Co-authored-by: kevin men <men_shi_bin@163.com> Co-authored-by: taos-support <it@taosdata.com> Co-authored-by: Yihao Deng <luomoxyz@126.com> Co-authored-by: Minglei Jin <49711132+stephenkgu@users.noreply.github.com> Co-authored-by: yihaoDeng <yhdeng@taosdata.com>
2895 lines
106 KiB
Python
2895 lines
106 KiB
Python
###################################################################
|
||
# Copyright (c) 2016 by TAOS Technologies, Inc.
|
||
# All rights reserved.
|
||
#
|
||
# This file is proprietary and confidential to TAOS Technologies.
|
||
# No part of this file may be reproduced, stored, transmitted,
|
||
# disclosed or used in any form or by any means other than as
|
||
# expressly provided by the written permission from Jianhui Tao
|
||
#
|
||
###################################################################
|
||
|
||
# -*- coding: utf-8 -*-
|
||
|
||
import sys
|
||
import os
|
||
import time
|
||
import datetime
|
||
import inspect
|
||
import traceback
|
||
import psutil
|
||
import shutil
|
||
import re
|
||
import pandas as pd
|
||
import csv
|
||
from .log import *
|
||
from .constant import *
|
||
import ctypes
|
||
import random
|
||
import datetime
|
||
import time
|
||
import taos
|
||
from tzlocal import get_localzone
|
||
from typing import Optional, Literal
|
||
|
||
|
||
def _parse_ns_timestamp(timestr):
|
||
dt_obj = datetime.datetime.strptime(
|
||
timestr[: len(timestr) - 3], "%Y-%m-%d %H:%M:%S.%f"
|
||
)
|
||
tz = (
|
||
int(
|
||
int(
|
||
(
|
||
dt_obj - datetime.datetime.fromtimestamp(0, dt_obj.tzinfo)
|
||
).total_seconds()
|
||
)
|
||
* 1e9
|
||
)
|
||
+ int(dt_obj.microsecond * 1000)
|
||
+ int(timestr[-3:])
|
||
)
|
||
return tz
|
||
|
||
|
||
def _parse_datetime(timestr):
|
||
"""
|
||
Parse a string to a datetime object. The string can be in one of the following formats:
|
||
The string can be in one of the following formats:
|
||
- '%Y-%m-%d %H:%M:%S.%f%z': Contains microseconds and timezone offset.
|
||
- '%Y-%m-%d %H:%M:%S%z': Contains no microseconds but contains timezone offset.
|
||
- '%Y-%m-%d %H:%M:%S.%f': Contains microseconds but no timezone offset.
|
||
- '%Y-%m-%d %H:%M:%S': Contains no microseconds and no timezone offset.
|
||
|
||
Args:
|
||
timestr (str): The string to be parsed.
|
||
|
||
Returns:
|
||
datetime.datetime: The datetime object parsed from the string.
|
||
"""
|
||
formats = [
|
||
"%Y-%m-%d %H:%M:%S.%f%z", # 包含微秒和时区偏移
|
||
"%Y-%m-%d %H:%M:%S%z", # 不包含微秒但包含时区偏移
|
||
"%Y-%m-%d %H:%M:%S.%f", # 包含微秒
|
||
"%Y-%m-%d %H:%M:%S", # 不包含微秒
|
||
]
|
||
|
||
for fmt in formats:
|
||
try:
|
||
# try to parse the string with the current format
|
||
dt = datetime.datetime.strptime(timestr, fmt)
|
||
# 如果字符串包含时区信息,则返回 aware 对象
|
||
# if sting contains timezone info, return aware object
|
||
if dt.tzinfo is not None:
|
||
return dt
|
||
|
||
else:
|
||
# if sting does not contain timezone info, assume it is in local timezone
|
||
# get local timezone
|
||
local_timezone = get_localzone()
|
||
# print("Timezone:", local_timezone)
|
||
return dt.replace(tzinfo=local_timezone)
|
||
except ValueError:
|
||
continue # if the current format does not match, try the next format
|
||
|
||
# 如果所有格式都不匹配,返回 None
|
||
# if none of the formats match, return
|
||
raise ValueError(
|
||
f"input format does not match. correct formats include: '{', '.join(formats)}'"
|
||
)
|
||
|
||
|
||
class TDSql:
|
||
def __init__(self):
|
||
self.queryRows = 0
|
||
self.queryCols = 0
|
||
self.affectedRows = 0
|
||
self.csvLine = 0
|
||
self.replica = 1
|
||
|
||
def init(self, cursor, log=False):
|
||
"""
|
||
Initializes the TDSql instance with a database cursor and optionally enables logging.
|
||
|
||
Args:
|
||
cursor: The database cursor to be used for executing SQL queries.
|
||
log (bool, optional): If True, enables logging of SQL statements to a file. Defaults to False.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
None
|
||
"""
|
||
self.cursor = cursor
|
||
self.sql = None
|
||
|
||
if log:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
self.cursor.log(caller.filename + ".sql")
|
||
|
||
def close(self):
|
||
"""
|
||
Closes the cursor.
|
||
|
||
Args:
|
||
None
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
None
|
||
"""
|
||
self.cursor.close()
|
||
|
||
def connect(self, username="root", passwd="taosdata", **kwargs):
|
||
"""
|
||
Reconnect
|
||
|
||
Args:
|
||
username (str):The username used to log in to the cluster.
|
||
passwd (str, optional): The password used to log in to the cluster.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
None
|
||
|
||
"""
|
||
|
||
self.cursor.close()
|
||
if not kwargs:
|
||
kwargs = {
|
||
'user': username,
|
||
'password': passwd,
|
||
}
|
||
else:
|
||
if 'user' not in kwargs:
|
||
kwargs['user'] = username
|
||
if 'password' not in kwargs:
|
||
kwargs['password'] = passwd
|
||
tdLog.debug(f"connect to {username}:{passwd} with kwargs: {kwargs}")
|
||
|
||
testconn = taos.connect(**kwargs)
|
||
self.cursor = testconn.cursor()
|
||
|
||
def prepare(self, dbname="db", drop=True, **kwargs):
|
||
"""
|
||
Prepares the database by optionally dropping it if it exists, creating it, and setting it as the active database.
|
||
|
||
Args:
|
||
dbname (str, optional): The name of the database to be prepared. Defaults to "db".
|
||
drop (bool, optional): If True, drops the database if it exists before creating it. Defaults to True.
|
||
**kwargs: Additional keyword arguments to be included in the database creation statement. If duration is not provided, it defaults to 100.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
None
|
||
"""
|
||
tdLog.debug(f"prepare database:{dbname}")
|
||
s = "reset query cache"
|
||
try:
|
||
self.cursor.execute(s)
|
||
except:
|
||
tdLog.notice("'reset query cache' is not supported")
|
||
if drop:
|
||
s = f"drop database if exists {dbname}"
|
||
self.cursor.execute(s)
|
||
s = f"create database {dbname}"
|
||
for k, v in kwargs.items():
|
||
if isinstance(v, str):
|
||
s += f" {k} '{v}'"
|
||
else:
|
||
s += f" {k} {v}"
|
||
|
||
if "duration" not in kwargs:
|
||
s += " duration 100"
|
||
if "replica" not in kwargs:
|
||
s += f" replica {self.replica}"
|
||
tdLog.debug(f"create database cmd: {s}")
|
||
self.cursor.execute(s)
|
||
|
||
s = f"use {dbname}"
|
||
self.cursor.execute(s)
|
||
time.sleep(2)
|
||
|
||
def queryAndCheckResult(self, sql_list, expect_result_list):
|
||
"""
|
||
Executes a list of SQL queries and checks the results against the expected results.
|
||
|
||
Args:
|
||
sql_list (list): The list of SQL queries to be executed.
|
||
expect_result_list (list): The list of expected results corresponding to each SQL query.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
Exception: If the execution of any SQL query fails or if the results do not match the expected results.
|
||
"""
|
||
try:
|
||
for index in range(len(sql_list)):
|
||
self.query(sql_list[index])
|
||
if len(expect_result_list[index]) == 0:
|
||
self.checkRows(0)
|
||
else:
|
||
self.checkRows(len(expect_result_list[index]))
|
||
for row in range(len(expect_result_list[index])):
|
||
for col in range(len(expect_result_list[index][row])):
|
||
self.checkData(
|
||
row, col, expect_result_list[index][row][col]
|
||
)
|
||
except Exception as ex:
|
||
raise (ex)
|
||
|
||
def query(
|
||
self,
|
||
sql,
|
||
row_tag=None,
|
||
queryTimes=10,
|
||
count_expected_res=None,
|
||
show=False,
|
||
exit=True,
|
||
):
|
||
"""
|
||
Executes a SQL query and fetches the results.
|
||
|
||
Args:
|
||
sql (str): The SQL query to be executed.
|
||
row_tag (optional): If provided, the method will return the fetched results. Defaults to None.
|
||
queryTimes (int, optional): The number of times to attempt the query in case of failure. Defaults to 10.
|
||
count_expected_res (optional): If provided, the method will repeatedly execute the query until the first result matches this value or the queryTimes limit is reached. Defaults to None.
|
||
show (bool, optional): If True, the SQL statement will be logged before execution. Defaults to False.
|
||
|
||
Returns:
|
||
int: The number of rows fetched if row_tag is not provided.
|
||
list: The fetched results if row_tag is provided.
|
||
|
||
Raises:
|
||
Exception: If the query fails after the specified number of attempts.
|
||
"""
|
||
if show:
|
||
tdLog.info(sql)
|
||
self.sql = sql
|
||
i = 1
|
||
while i <= queryTimes:
|
||
try:
|
||
self.cursor.execute(sql)
|
||
self.queryResult = self.cursor.fetchall()
|
||
self.queryRows = len(self.queryResult)
|
||
self.queryCols = len(self.cursor.description)
|
||
|
||
if count_expected_res is not None:
|
||
counter = 0
|
||
while count_expected_res != self.queryResult[0][0]:
|
||
self.cursor.execute(sql)
|
||
self.queryResult = self.cursor.fetchall()
|
||
if counter < queryTimes:
|
||
counter += 0.5
|
||
time.sleep(0.5)
|
||
else:
|
||
return False
|
||
if row_tag:
|
||
return self.queryResult
|
||
return self.queryRows
|
||
except Exception as e:
|
||
if exit:
|
||
tdLog.notice("Try to query again, query times: %d " % i)
|
||
if i == queryTimes:
|
||
if exit:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, sql, repr(e))
|
||
tdLog.error("%s(%d) failed: sql:%s, %s" % args)
|
||
raise Exception(repr(e))
|
||
else:
|
||
return False
|
||
i += 1
|
||
time.sleep(1)
|
||
pass
|
||
|
||
def querySuccessailed(
|
||
self,
|
||
sql,
|
||
row_tag=None,
|
||
queryTimes=10,
|
||
count_expected_res=None,
|
||
expectErrInfo=None,
|
||
fullMatched=True,
|
||
):
|
||
self.sql = sql
|
||
i = 1
|
||
while i <= queryTimes:
|
||
try:
|
||
self.cursor.execute(sql)
|
||
self.queryResult = self.cursor.fetchall()
|
||
self.queryRows = len(self.queryResult)
|
||
self.queryCols = len(self.cursor.description)
|
||
|
||
if count_expected_res is not None:
|
||
counter = 0
|
||
while count_expected_res != self.queryResult[0][0]:
|
||
self.cursor.execute(sql)
|
||
self.queryResult = self.cursor.fetchall()
|
||
if counter < queryTimes:
|
||
counter += 0.5
|
||
time.sleep(0.5)
|
||
else:
|
||
return False
|
||
|
||
tdLog.info("query is success")
|
||
time.sleep(1)
|
||
continue
|
||
except Exception as e:
|
||
tdLog.notice("Try to query again, query times: %d " % i)
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
if i < queryTimes:
|
||
error_info = repr(e)
|
||
print(error_info)
|
||
self.error_info = ",".join(
|
||
error_info[error_info.index("(") + 1 : -1].split(",")[:-1]
|
||
).replace("'", "")
|
||
self.queryRows = 0
|
||
self.queryCols = 0
|
||
self.queryResult = None
|
||
|
||
if fullMatched:
|
||
if expectErrInfo != None:
|
||
if expectErrInfo == self.error_info:
|
||
tdLog.info(
|
||
"sql:%s, expected expectErrInfo '%s' occured"
|
||
% (sql, expectErrInfo)
|
||
)
|
||
else:
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, expectErrInfo '%s' occured, but not expected expectErrInfo '%s'"
|
||
% (
|
||
caller.filename,
|
||
caller.lineno,
|
||
sql,
|
||
self.error_info,
|
||
expectErrInfo,
|
||
)
|
||
)
|
||
else:
|
||
if expectErrInfo != None:
|
||
if expectErrInfo in self.error_info:
|
||
tdLog.info(
|
||
"sql:%s, expected expectErrInfo '%s' occured"
|
||
% (sql, expectErrInfo)
|
||
)
|
||
else:
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, expectErrInfo %s occured, but not expected expectErrInfo '%s'"
|
||
% (
|
||
caller.filename,
|
||
caller.lineno,
|
||
sql,
|
||
self.error_info,
|
||
expectErrInfo,
|
||
)
|
||
)
|
||
|
||
return self.error_info
|
||
elif i == queryTimes:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, sql, repr(e))
|
||
tdLog.notice("%s(%d) failed: sql:%s, %s" % args)
|
||
raise Exception(repr(e))
|
||
i += 1
|
||
time.sleep(1)
|
||
pass
|
||
|
||
def executeTimes(self, sql, times):
|
||
"""
|
||
Executes a SQL statement a specified number of times.(Not used)
|
||
|
||
Args:
|
||
sql (str): The SQL statement to be executed.
|
||
times (int): The number of times to execute the SQL statement.
|
||
|
||
Returns:
|
||
int: The number of affected rows from the last execution.
|
||
|
||
Raises:
|
||
None
|
||
"""
|
||
for i in range(times):
|
||
try:
|
||
return self.cursor.execute(sql)
|
||
except BaseException:
|
||
time.sleep(1)
|
||
continue
|
||
|
||
def execute(self, sql, queryTimes=10, show=False):
|
||
"""
|
||
Executes a SQL statement.
|
||
|
||
Args:
|
||
sql (str): The SQL statement to be executed.
|
||
queryTimes (int, optional): The number of times to attempt the execution in case of failure. Defaults to 10.
|
||
show (bool, optional): If True, the SQL statement will be logged before execution. Defaults to False.
|
||
|
||
Returns:
|
||
int: The number of affected rows.
|
||
|
||
Raises:
|
||
Exception: If the execution fails after the specified number of attempts.
|
||
"""
|
||
self.sql = sql
|
||
if show:
|
||
tdLog.info(sql)
|
||
i = 1
|
||
while i <= queryTimes:
|
||
try:
|
||
self.affectedRows = self.cursor.execute(sql)
|
||
return self.affectedRows
|
||
except Exception as e:
|
||
tdLog.notice("Try to execute sql again, execute times: %d " % i)
|
||
if i == queryTimes:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, sql, repr(e))
|
||
tdLog.notice("%s(%d) failed: sql:%s, %s" % args)
|
||
raise Exception(repr(e))
|
||
i += 1
|
||
time.sleep(1)
|
||
pass
|
||
|
||
# execute many sql
|
||
def executes(self, sqls, queryTimes=30, show=False):
|
||
"""
|
||
Executes a list of SQL statements.
|
||
|
||
Args:
|
||
sqls (list): The list of SQL statements to be executed.
|
||
queryTimes (int, optional): The number of times to attempt the execution in case of failure. Defaults to 30.
|
||
show (bool, optional): If True, each SQL statement will be logged before execution. Defaults to False.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
Exception: If the execution of any SQL statement fails after the specified number of attempts.
|
||
"""
|
||
for sql in sqls:
|
||
self.execute(sql, queryTimes, show)
|
||
|
||
def waitedQuery(self, sql, expectedRows, timeout):
|
||
"""
|
||
Executes a SQL query and waits until the expected number of rows is retrieved or the timeout is reached.
|
||
|
||
Args:
|
||
sql (str): The SQL query to be executed.
|
||
expectedRows (int): The expected number of rows to be retrieved.
|
||
timeout (int): The maximum time to wait (in seconds) for the expected number of rows to be retrieved.
|
||
|
||
Returns:
|
||
tuple: A tuple containing the number of rows retrieved and the time taken (in seconds).
|
||
|
||
Raises:
|
||
Exception: If the query execution fails.
|
||
"""
|
||
tdLog.info(
|
||
"sql: %s, try to retrieve %d rows in %d seconds"
|
||
% (sql, expectedRows, timeout)
|
||
)
|
||
self.sql = sql
|
||
try:
|
||
for i in range(timeout):
|
||
self.cursor.execute(sql)
|
||
self.queryResult = self.cursor.fetchall()
|
||
self.queryRows = len(self.queryResult)
|
||
self.queryCols = len(self.cursor.description)
|
||
tdLog.info(
|
||
"sql: %s, try to retrieve %d rows,get %d rows"
|
||
% (sql, expectedRows, self.queryRows)
|
||
)
|
||
if self.queryRows >= expectedRows:
|
||
return (self.queryRows, i)
|
||
time.sleep(1)
|
||
except Exception as e:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, sql, repr(e))
|
||
tdLog.notice("%s(%d) failed: sql:%s, %s" % args)
|
||
raise Exception(repr(e))
|
||
return (self.queryRows, timeout)
|
||
|
||
def query_success_failed(self, sql, row_tag=None, queryTimes=10, count_expected_res=None, expectErrInfo = None, fullMatched = True):
|
||
"""Executes a SQL query with retry mechanism and handles both successful and failed scenarios.
|
||
|
||
This method attempts to execute a SQL query multiple times, handling both successful
|
||
executions and expected error conditions. It's particularly useful for testing
|
||
scenarios where queries might initially fail but eventually succeed, or for
|
||
validating specific error conditions.
|
||
|
||
Args:
|
||
sql (str): The SQL query statement to be executed.
|
||
row_tag (optional): If provided, the method will return the fetched results
|
||
instead of just the row count. Defaults to None.
|
||
queryTimes (int, optional): Maximum number of retry attempts if the query fails.
|
||
Defaults to 10.
|
||
count_expected_res (optional): If provided, the method will repeatedly execute
|
||
the query until the first result matches this value
|
||
or retry limit is reached. Defaults to None.
|
||
expectErrInfo (str, optional): Expected error message to validate against when
|
||
query fails. If None, any error is acceptable.
|
||
Defaults to None.
|
||
fullMatched (bool, optional): If True, performs exact string matching for error
|
||
messages. If False, performs partial string matching
|
||
(contains). Defaults to True.
|
||
|
||
Returns:
|
||
str: Error information string if an expected error occurs and query fails
|
||
within retry attempts.
|
||
None: If query succeeds or if unexpected error occurs and reaches retry limit.
|
||
|
||
Raises:
|
||
Exception: If query fails after all retry attempts and the error is not expected
|
||
or doesn't match the expected error pattern.
|
||
"""
|
||
self.sql = sql
|
||
i=1
|
||
while i <= queryTimes:
|
||
try:
|
||
self.cursor.execute(sql)
|
||
self.queryResult = self.cursor.fetchall()
|
||
self.queryRows = len(self.queryResult)
|
||
self.queryCols = len(self.cursor.description)
|
||
|
||
if count_expected_res is not None:
|
||
counter = 0
|
||
while count_expected_res != self.queryResult[0][0]:
|
||
self.cursor.execute(sql)
|
||
self.queryResult = self.cursor.fetchall()
|
||
if counter < queryTimes:
|
||
counter += 0.5
|
||
time.sleep(0.5)
|
||
else:
|
||
return False
|
||
|
||
tdLog.info("query is success")
|
||
time.sleep(1)
|
||
continue
|
||
except Exception as e:
|
||
tdLog.notice("Try to query again, query times: %d "%i)
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
if i < queryTimes:
|
||
error_info = repr(e)
|
||
print(error_info)
|
||
self.error_info = ','.join(error_info[error_info.index('(')+1:-1].split(",")[:-1]).replace("'","")
|
||
self.queryRows = 0
|
||
self.queryCols = 0
|
||
self.queryResult = None
|
||
|
||
if fullMatched:
|
||
if expectErrInfo != None:
|
||
if expectErrInfo == self.error_info:
|
||
tdLog.info("sql:%s, expected expectErrInfo '%s' occured" % (sql, expectErrInfo))
|
||
else:
|
||
tdLog.exit("%s(%d) failed: sql:%s, expectErrInfo '%s' occured, but not expected expectErrInfo '%s'" % (caller.filename, caller.lineno, sql, self.error_info, expectErrInfo))
|
||
else:
|
||
if expectErrInfo != None:
|
||
if expectErrInfo in self.error_info:
|
||
tdLog.info("sql:%s, expected expectErrInfo '%s' occured" % (sql, expectErrInfo))
|
||
else:
|
||
tdLog.exit("%s(%d) failed: sql:%s, expectErrInfo %s occured, but not expected expectErrInfo '%s'" % (caller.filename, caller.lineno, sql, self.error_info, expectErrInfo))
|
||
|
||
return self.error_info
|
||
elif i == queryTimes:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, sql, repr(e))
|
||
tdLog.notice("%s(%d) failed: sql:%s, %s" % args)
|
||
raise Exception(repr(e))
|
||
i+=1
|
||
time.sleep(1)
|
||
pass
|
||
|
||
def isErrorSql(self, sql):
|
||
"""
|
||
Executes a SQL statement and checks if it results in an error.(Not used)
|
||
|
||
Args:
|
||
sql (str): The SQL statement to be executed.
|
||
|
||
Returns:
|
||
bool: True if the SQL statement results in an error, False otherwise.
|
||
|
||
Raises:
|
||
None
|
||
"""
|
||
err_flag = True
|
||
try:
|
||
self.cursor.execute(sql)
|
||
except BaseException:
|
||
err_flag = False
|
||
|
||
return False if err_flag else True
|
||
|
||
def errors(
|
||
self, sql_list, expected_error_id_list=None, expected_error_info_list=None
|
||
):
|
||
"""
|
||
Executes a list of SQL queries and checks for expected errors.
|
||
|
||
Args:
|
||
sql_list (list): The list of SQL queries to be executed.
|
||
expected_error_id_list (list, optional): The list of expected error numbers corresponding to each SQL query. Defaults to None.
|
||
expected_error_info_list (list, optional): The list of expected error information corresponding to each SQL query. Defaults to None.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
SystemExit: If the SQL list is empty, if the execution of any SQL query fails, if the expected error does not occur, or if the error information does not match the expected information.
|
||
"""
|
||
try:
|
||
if len(sql_list) > 0:
|
||
for i in range(len(sql_list)):
|
||
if expected_error_id_list and expected_error_info_list:
|
||
self.error(
|
||
sql_list[i],
|
||
expected_error_id_list[i],
|
||
expected_error_info_list[i],
|
||
)
|
||
elif expected_error_id_list:
|
||
self.error(sql_list[i], expectedErrno=expected_error_id_list[i])
|
||
elif expected_error_info_list:
|
||
self.error(
|
||
sql_list[i], expectErrInfo=expected_error_info_list[i]
|
||
)
|
||
else:
|
||
self.error(sql_list[i])
|
||
else:
|
||
tdLog.exit("sql list is empty")
|
||
except Exception as ex:
|
||
tdLog.exit("Failed to execute sql list: %s, error: %s" % (sql_list, ex))
|
||
|
||
def is_err_sql(self, sql):
|
||
"""Checks if a SQL statement will result in an error when executed.
|
||
|
||
This method executes the provided SQL statement and determines whether it
|
||
causes an exception. It's useful for testing error conditions and validating
|
||
that certain SQL statements should fail.
|
||
|
||
Args:
|
||
sql (str): The SQL statement to be tested for errors.
|
||
|
||
Returns:
|
||
bool: False if the SQL statement executes successfully without errors,
|
||
True if the SQL statement results in an error/exception.
|
||
|
||
Raises:
|
||
None: This method catches all exceptions internally and returns a boolean
|
||
result instead of raising exceptions.
|
||
"""
|
||
err_flag = True
|
||
try:
|
||
self.cursor.execute(sql)
|
||
except BaseException:
|
||
err_flag = False
|
||
|
||
return False if err_flag else True
|
||
|
||
def no_error(self, sql):
|
||
"""_summary_
|
||
|
||
Args:
|
||
sql (_type_): _description_
|
||
"""
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
expectErrOccurred = False
|
||
|
||
try:
|
||
self.cursor.execute(sql)
|
||
except BaseException as e:
|
||
expectErrOccurred = True
|
||
self.errno = e.errno
|
||
error_info = repr(e)
|
||
self.error_info = ','.join(error_info[error_info.index('(') + 1:-1].split(",")[:-1]).replace("'", "")
|
||
|
||
if expectErrOccurred:
|
||
tdLog.exit("%s(%d) failed: sql:%s, unexpect error '%s' occurred" % (caller.filename, caller.lineno, sql, self.error_info))
|
||
else:
|
||
tdLog.info("sql:%s, check passed, no ErrInfo occurred" % (sql))
|
||
|
||
def error(
|
||
self, sql, expectedErrno=None, expectErrInfo=None, fullMatched=True, show=False
|
||
):
|
||
"""
|
||
Executes a SQL statement and checks for expected errors.
|
||
|
||
Args:
|
||
sql (str): The SQL statement to be executed.
|
||
expectedErrno (int, optional): The expected error number. Defaults to None.
|
||
expectErrInfo (str, optional): The expected error information. Defaults to None.
|
||
fullMatched (bool, optional): If True, checks for exact matches of the expected error information. Defaults to True.
|
||
show (bool, optional): If True, the SQL statement will be logged before execution. Defaults to False.
|
||
|
||
Returns:
|
||
str: The error information if an error occurs.
|
||
|
||
Raises:
|
||
SystemExit: If the expected error does not occur or if the error information does not match the expected information.
|
||
"""
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
expectErrNotOccured = True
|
||
if show:
|
||
tdLog.info(sql)
|
||
|
||
try:
|
||
self.cursor.execute(sql)
|
||
self.queryResult = self.cursor.fetchall()
|
||
self.queryRows = len(self.queryResult)
|
||
self.queryCols = len(self.cursor.description)
|
||
except BaseException as e:
|
||
expectErrNotOccured = False
|
||
self.errno = e.errno
|
||
error_info = repr(e)
|
||
self.error_info = ",".join(
|
||
error_info[error_info.index("(") + 1 : -1].split(",")[:-1]
|
||
).replace("'", "")
|
||
# self.error_info = (','.join(error_info.split(",")[:-1]).split("(",1)[1:][0]).replace("'","")
|
||
if expectErrNotOccured:
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, expect error not occured"
|
||
% (caller.filename, caller.lineno, sql)
|
||
)
|
||
else:
|
||
self.queryRows = 0
|
||
self.queryCols = 0
|
||
self.queryResult = None
|
||
if fullMatched:
|
||
if expectedErrno != None:
|
||
expectedErrno_rest = expectedErrno & 0x0000FFFF
|
||
if expectedErrno == self.errno or expectedErrno_rest == self.errno:
|
||
tdLog.info(
|
||
"sql:%s, expected errno %s occured" % (sql, expectedErrno)
|
||
)
|
||
else:
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, errno '%s' occured, but not expected errno '%s'"
|
||
% (
|
||
caller.filename,
|
||
caller.lineno,
|
||
sql,
|
||
self.errno,
|
||
expectedErrno,
|
||
)
|
||
)
|
||
|
||
if expectErrInfo != None:
|
||
if expectErrInfo == self.error_info:
|
||
tdLog.info(
|
||
"sql:%s, expected ErrInfo '%s' occured"
|
||
% (sql, expectErrInfo)
|
||
)
|
||
else:
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, ErrInfo '%s' occured, but not expected ErrInfo '%s'"
|
||
% (
|
||
caller.filename,
|
||
caller.lineno,
|
||
sql,
|
||
self.error_info,
|
||
expectErrInfo,
|
||
)
|
||
)
|
||
else:
|
||
if expectedErrno != None:
|
||
expectedErrno_rest = expectedErrno & 0x0000FFFF
|
||
if expectedErrno in self.errno or expectedErrno_rest in self.errno:
|
||
tdLog.info(
|
||
"sql:%s, expected errno %s occured" % (sql, expectedErrno)
|
||
)
|
||
else:
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, errno '%s' occured, but not expected errno '%s'"
|
||
% (
|
||
caller.filename,
|
||
caller.lineno,
|
||
sql,
|
||
self.errno,
|
||
expectedErrno,
|
||
)
|
||
)
|
||
|
||
if expectErrInfo != None:
|
||
if expectErrInfo in self.error_info:
|
||
tdLog.info(
|
||
"sql:%s, expected ErrInfo '%s' occured"
|
||
% (sql, expectErrInfo)
|
||
)
|
||
else:
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, ErrInfo %s occured, but not expected ErrInfo '%s'"
|
||
% (
|
||
caller.filename,
|
||
caller.lineno,
|
||
sql,
|
||
self.error_info,
|
||
expectErrInfo,
|
||
)
|
||
)
|
||
|
||
return self.error_info
|
||
|
||
def noError(self, sql):
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
expectErrOccurred = False
|
||
|
||
try:
|
||
self.cursor.execute(sql)
|
||
except BaseException as e:
|
||
expectErrOccurred = True
|
||
self.errno = e.errno
|
||
error_info = repr(e)
|
||
self.error_info = ",".join(
|
||
error_info[error_info.index("(") + 1 : -1].split(",")[:-1]
|
||
).replace("'", "")
|
||
|
||
if expectErrOccurred:
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, unexpect error '%s' occurred"
|
||
% (caller.filename, caller.lineno, sql, self.error_info)
|
||
)
|
||
else:
|
||
tdLog.info("sql:%s, check passed, no ErrInfo occurred" % (sql))
|
||
|
||
def getData(self, row, col):
|
||
"""
|
||
Retrieves the data at the specified row and column from the last query result.
|
||
|
||
Args:
|
||
row (int): The row index of the data to be retrieved.
|
||
col (int): The column index of the data to be retrieved.
|
||
|
||
Returns:
|
||
The data at the specified row and column.
|
||
|
||
Raises:
|
||
SystemExit: If the specified row or column is out of range.
|
||
"""
|
||
self.checkRowCol(row, col)
|
||
return self.queryResult[row][col]
|
||
|
||
def getColData(self, col):
|
||
"""
|
||
Retrieves all data from the specified column in the last query result.
|
||
|
||
Args:
|
||
col (int): The column index of the data to be retrieved.
|
||
|
||
Returns:
|
||
list: A list containing all data from the specified column.
|
||
|
||
Raises:
|
||
None
|
||
"""
|
||
colDatas = []
|
||
for i in range(self.queryRows):
|
||
colDatas.append(self.queryResult[i][col])
|
||
return colDatas
|
||
|
||
def getRowData(self, row):
|
||
"""
|
||
Retrieves all data from the specified row in the last query result.
|
||
|
||
Args:
|
||
row (int): The row index of the data to be retrieved.
|
||
|
||
Returns:
|
||
list: A list containing all data from the specified row.
|
||
|
||
Raises:
|
||
SystemExit: If the specified row is out of range.
|
||
"""
|
||
if row >= self.queryRows:
|
||
return None
|
||
|
||
return self.queryResult[row]
|
||
|
||
def getResult(self, sql, exit=True):
|
||
"""
|
||
Executes a SQL query and fetches the results.
|
||
|
||
Args:
|
||
sql (str): The SQL query to be executed.
|
||
|
||
Returns:
|
||
list: The fetched results.
|
||
|
||
Raises:
|
||
Exception: If the query execution fails.
|
||
"""
|
||
self.sql = sql
|
||
try:
|
||
self.cursor.execute(sql)
|
||
self.queryResult = self.cursor.fetchall()
|
||
except Exception as e:
|
||
if exit:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, sql, repr(e))
|
||
tdLog.notice("%s(%d) failed: sql:%s, %s" % args)
|
||
raise Exception(repr(e))
|
||
else:
|
||
return []
|
||
return self.queryResult
|
||
|
||
def getVariable(self, search_attr):
|
||
"""
|
||
Retrieves the value of a specified variable from the database.
|
||
|
||
Args:
|
||
search_attr (str): The name of the variable to be retrieved.
|
||
|
||
Returns:
|
||
tuple: A tuple containing the value of the specified variable and the list of all variables.
|
||
|
||
Raises:
|
||
Exception: If the query execution fails.
|
||
"""
|
||
try:
|
||
sql = "show variables"
|
||
param_list = self.query(sql, row_tag=True)
|
||
for param in param_list:
|
||
if param[0] == search_attr:
|
||
return param[1], param_list
|
||
except Exception as e:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, sql, repr(e))
|
||
tdLog.notice("%s(%d) failed: sql:%s, %s" % args)
|
||
raise Exception(repr(e))
|
||
|
||
def getColNameList(self, sql, col_tag=None):
|
||
"""
|
||
Executes a SQL query and retrieves the column names and optionally the column types.
|
||
|
||
Args:
|
||
sql (str): The SQL query to be executed.
|
||
col_tag (optional): If provided, the method will return both column names and column types. Defaults to None.
|
||
|
||
Returns:
|
||
list: A list containing the column names.
|
||
tuple: A tuple containing two lists - the column names and the column types, if col_tag is provided.
|
||
|
||
Raises:
|
||
Exception: If the query execution fails.
|
||
"""
|
||
self.sql = sql
|
||
try:
|
||
col_name_list = []
|
||
col_type_list = []
|
||
self.cursor.execute(sql)
|
||
for query_col in self.cursor.description:
|
||
col_name_list.append(query_col[0])
|
||
col_type_list.append(query_col[1])
|
||
except Exception as e:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, sql, repr(e))
|
||
tdLog.notice("%s(%d) failed: sql:%s, %s" % args)
|
||
raise Exception(repr(e))
|
||
if col_tag:
|
||
return col_name_list, col_type_list
|
||
return col_name_list
|
||
|
||
def getRows(self):
|
||
"""
|
||
Retrieves the number of rows fetched by the last query.
|
||
|
||
Args:
|
||
None
|
||
|
||
Returns:
|
||
int: The number of rows fetched by the last query.
|
||
|
||
Raises:
|
||
None
|
||
"""
|
||
return self.queryRows
|
||
|
||
def getCols(self):
|
||
"""
|
||
Retrieves the number of cols fetched by the last query.
|
||
|
||
Args:
|
||
None
|
||
|
||
Returns:
|
||
int: The number of cols fetched by the last query.
|
||
|
||
Raises:
|
||
None
|
||
"""
|
||
return self.queryCols
|
||
|
||
# get first value
|
||
def getFirstValue(self, sql):
|
||
"""
|
||
Executes a SQL query and retrieves the first value in the result.
|
||
|
||
Args:
|
||
sql (str): The SQL query to be executed.
|
||
|
||
Returns:
|
||
The first value in the result.
|
||
|
||
Raises:
|
||
Exception: If the query execution fails.
|
||
"""
|
||
self.query(sql)
|
||
return self.getData(0, 0)
|
||
|
||
#
|
||
# check session
|
||
#
|
||
|
||
def checkRows(self, expectedRows, show=False):
|
||
"""
|
||
Checks if the number of rows fetched by the last query matches the expected number of rows.
|
||
|
||
Args:
|
||
expectedRows (int): The expected number of rows.
|
||
|
||
Returns:
|
||
bool: True if the number of rows matches the expected number, otherwise it exits the program.
|
||
|
||
Raises:
|
||
SystemExit: If the number of rows does not match the expected number.
|
||
"""
|
||
return self.checkEqual(self.queryRows, expectedRows, show=show)
|
||
|
||
def checkRowsV2(
|
||
self,
|
||
expectedRows: int,
|
||
operator: Literal["<", "<=", ">", ">=", "==", "!="] = "==",
|
||
show: bool = True,
|
||
) -> bool:
|
||
"""
|
||
Verify if the number of rows returned by SQL query meets the expected condition.
|
||
|
||
Args:
|
||
expectedRows : int
|
||
The expected number of rows to compare against
|
||
operator : str, optional
|
||
Comparison operator ('<', '<=', '>', '>=', '==', '!='),
|
||
defaults to '<'
|
||
show : bool, optional
|
||
Whether to print the verification result, defaults to True
|
||
|
||
Returns:
|
||
bool
|
||
True if the actual row count meets the expected condition,
|
||
False otherwise
|
||
|
||
Raises:
|
||
ValueError: If invalid operator is provided
|
||
|
||
Usage:
|
||
assert checker.checkRows(15, operator="<") # Verify if less than 15 rows
|
||
"""
|
||
actualRows = self.queryRows
|
||
|
||
try:
|
||
# Perform comparison based on operator
|
||
if operator == "<":
|
||
result = actualRows < expectedRows
|
||
elif operator == "<=":
|
||
result = actualRows <= expectedRows
|
||
elif operator == ">":
|
||
result = actualRows > expectedRows
|
||
elif operator == ">=":
|
||
result = actualRows >= expectedRows
|
||
elif operator == "==":
|
||
result = actualRows == expectedRows
|
||
elif operator == "!=":
|
||
result = actualRows != expectedRows
|
||
else:
|
||
raise ValueError(f"Unsupported comparison operator: {operator}")
|
||
|
||
if show:
|
||
tdLog.info(
|
||
f"Actual rows: {actualRows}, expected rows: {expectedRows}, comparison: {operator}, result: {'PASS' if result else 'FAIL'}"
|
||
)
|
||
return result
|
||
|
||
except Exception as e:
|
||
tdLog.error(f"checkRows failed: {str(e)}")
|
||
return False
|
||
|
||
def checkRowsNotExited(self, expectedRows):
|
||
"""
|
||
Check if the query rows is equal to the expected rows
|
||
|
||
Args:
|
||
expectedRows: The expected number of rows.
|
||
|
||
Returns:
|
||
bool: Returns True if the actual number of rows matches the expected number, otherwise returns False.
|
||
"""
|
||
if self.queryRows == expectedRows:
|
||
return True
|
||
else:
|
||
return False
|
||
|
||
def checkRowsRange(self, excepte_row_list):
|
||
"""
|
||
Checks if the number of rows fetched by the last query is within the expected range.(Not used)
|
||
|
||
Args:
|
||
excepte_row_list (list): A list of expected row counts.
|
||
|
||
Returns:
|
||
bool: True if the number of rows is within the expected range, otherwise it exits the program.
|
||
|
||
Raises:
|
||
SystemExit: If the number of rows is not within the expected range.
|
||
"""
|
||
if self.queryRows in excepte_row_list:
|
||
tdLog.info(
|
||
f"sql:{self.sql}, queryRows:{self.queryRows} in expect:{excepte_row_list}"
|
||
)
|
||
return True
|
||
else:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
tdLog.exit(
|
||
f"{caller.filename}({caller.lineno}) failed: sql:{self.sql}, queryRows:{self.queryRows} not in expect:{excepte_row_list}"
|
||
)
|
||
|
||
def checkCols(self, expectCols):
|
||
"""
|
||
Checks if the number of columns fetched by the last query matches the expected number of columns.
|
||
|
||
Args:
|
||
expectCols (int): The expected number of columns.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
SystemExit: If the number of columns does not match the expected number.
|
||
"""
|
||
if self.queryCols == expectCols:
|
||
tdLog.info(
|
||
"sql:%s, queryCols:%d == expect:%d"
|
||
% (self.sql, self.queryCols, expectCols)
|
||
)
|
||
else:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (
|
||
caller.filename,
|
||
caller.lineno,
|
||
self.sql,
|
||
self.queryCols,
|
||
expectCols,
|
||
)
|
||
tdLog.exit("%s(%d) failed: sql:%s, queryCols:%d != expect:%d" % args)
|
||
|
||
def checkRowCol(self, row, col):
|
||
"""
|
||
Checks if the specified row and column indices are within the range of the last query result.
|
||
|
||
Args:
|
||
row (int): The row index to be checked.
|
||
col (int): The column index to be checked.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
SystemExit: If the specified row or column index is out of range.
|
||
"""
|
||
caller = inspect.getframeinfo(inspect.stack()[2][0])
|
||
if row < 0:
|
||
args = (caller.filename, caller.lineno, self.sql, row)
|
||
tdLog.exit("%s(%d) failed: sql:%s, row:%d is smaller than zero" % args)
|
||
if col < 0:
|
||
args = (caller.filename, caller.lineno, self.sql, row)
|
||
tdLog.exit("%s(%d) failed: sql:%s, col:%d is smaller than zero" % args)
|
||
if row > self.queryRows:
|
||
args = (caller.filename, caller.lineno, self.sql, row, self.queryRows)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, row:%d is larger than queryRows:%d" % args
|
||
)
|
||
if col > self.queryCols:
|
||
args = (caller.filename, caller.lineno, self.sql, col, self.queryCols)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, col:%d is larger than queryCols:%d" % args
|
||
)
|
||
|
||
def checkDataType(self, row, col, dataType):
|
||
"""
|
||
Checks if the data type at the specified row and column matches the expected data type.
|
||
|
||
Args:
|
||
row (int): The row index of the data to be checked.
|
||
col (int): The column index of the data to be checked.
|
||
dataType (str): The expected data type.
|
||
|
||
Returns:
|
||
bool: True if the data type matches the expected data type, otherwise False.
|
||
|
||
Raises:
|
||
SystemExit: If the specified row or column index is out of range.
|
||
"""
|
||
self.checkRowCol(row, col)
|
||
return self.cursor.istype(col, dataType)
|
||
|
||
def checkFloatString(self, row, col, data, show=False):
|
||
if row >= self.queryRows:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, row + 1, self.queryRows)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, row:%d is larger than queryRows:%d" % args
|
||
)
|
||
if col >= self.queryCols:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, col + 1, self.queryCols)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, col:%d is larger than queryCols:%d" % args
|
||
)
|
||
|
||
self.checkRowCol(row, col)
|
||
|
||
val = float(self.queryResult[row][col])
|
||
if abs(data) >= 1 and abs((val - data) / data) <= 0.000001:
|
||
if show:
|
||
tdLog.info("check successfully")
|
||
elif abs(data) < 1 and abs(val - data) <= 0.000001:
|
||
if show:
|
||
tdLog.info("check successfully")
|
||
else:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (
|
||
caller.filename,
|
||
caller.lineno,
|
||
self.sql,
|
||
row,
|
||
col,
|
||
self.queryResult[row][col],
|
||
data,
|
||
)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s row:%d col:%d data:%s != expect:%s" % args
|
||
)
|
||
|
||
def compareData(self, row, col, data, show=False):
|
||
return self.checkData(row, col, data, show, False)
|
||
|
||
def checkData(self, row, col, data, show=False, exit=True):
|
||
"""
|
||
Checks if the data at the specified row and column matches the expected data.
|
||
|
||
Args:
|
||
row (int): The row index of the data to be checked.
|
||
col (int): The column index of the data to be checked.
|
||
data: The expected data to be compared with.
|
||
show (bool, optional): If True, logs a message when the check is successful. Defaults to False.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
SystemExit: If the data at the specified row and column does not match the expected data.
|
||
"""
|
||
if row >= self.queryRows:
|
||
if exit:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (
|
||
caller.filename,
|
||
caller.lineno,
|
||
self.sql,
|
||
row + 1,
|
||
self.queryRows,
|
||
)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, row:%d is larger than queryRows:%d" % args
|
||
)
|
||
else:
|
||
return False
|
||
if col >= self.queryCols:
|
||
if exit:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (
|
||
caller.filename,
|
||
caller.lineno,
|
||
self.sql,
|
||
col + 1,
|
||
self.queryCols,
|
||
)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, col:%d is larger than queryCols:%d" % args
|
||
)
|
||
else:
|
||
return False
|
||
|
||
self.checkRowCol(row, col)
|
||
|
||
if self.queryResult[row][col] != data:
|
||
if self.cursor.istype(col, "TIMESTAMP"):
|
||
# suppose user want to check nanosecond timestamp if a longer data passed``
|
||
if isinstance(data, str):
|
||
if len(data) >= 28:
|
||
if self.queryResult[row][col] == _parse_ns_timestamp(data):
|
||
if show:
|
||
tdLog.info("check successfully")
|
||
else:
|
||
if exit:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (
|
||
caller.filename,
|
||
caller.lineno,
|
||
self.sql,
|
||
row,
|
||
col,
|
||
self.queryResult[row][col],
|
||
data,
|
||
)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s row:%d col:%d data:%s != expect:%s"
|
||
% args
|
||
)
|
||
else:
|
||
return False
|
||
else:
|
||
# print(f"{self.queryResult[row][col]}")
|
||
real = self.queryResult[row][col]
|
||
if real is None:
|
||
# none
|
||
if str(real) == data:
|
||
if show:
|
||
tdLog.info("check successfully")
|
||
elif real.astimezone(datetime.timezone.utc) == _parse_datetime(
|
||
data
|
||
).astimezone(datetime.timezone.utc):
|
||
# tdLog.info(f"sql:{self.sql}, row:{row} col:{col} data:{self.queryResult[row][col]} == expect:{data}")
|
||
if show:
|
||
tdLog.info("check successfully")
|
||
else:
|
||
if exit:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (
|
||
caller.filename,
|
||
caller.lineno,
|
||
self.sql,
|
||
row,
|
||
col,
|
||
self.queryResult[row][col],
|
||
data,
|
||
)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s row:%d col:%d data:%s != expect:%s"
|
||
% args
|
||
)
|
||
else:
|
||
return False
|
||
return True
|
||
elif isinstance(data, int):
|
||
if len(str(data)) == 16:
|
||
precision = "us"
|
||
elif len(str(data)) == 13:
|
||
precision = "ms"
|
||
elif len(str(data)) == 19:
|
||
precision = "ns"
|
||
else:
|
||
if exit:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (
|
||
caller.filename,
|
||
caller.lineno,
|
||
self.sql,
|
||
row,
|
||
col,
|
||
self.queryResult[row][col],
|
||
data,
|
||
)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s row:%d col:%d data:%s != expect:%s"
|
||
% args
|
||
)
|
||
else:
|
||
return False
|
||
success = False
|
||
if precision == "ms":
|
||
dt_obj = self.queryResult[row][col]
|
||
ts = int(
|
||
int(
|
||
(
|
||
dt_obj
|
||
- datetime.datetime.fromtimestamp(0, dt_obj.tzinfo)
|
||
).total_seconds()
|
||
)
|
||
* 1000
|
||
) + int(dt_obj.microsecond / 1000)
|
||
if ts == data:
|
||
success = True
|
||
elif precision == "us":
|
||
dt_obj = self.queryResult[row][col]
|
||
ts = int(
|
||
int(
|
||
(
|
||
dt_obj
|
||
- datetime.datetime.fromtimestamp(0, dt_obj.tzinfo)
|
||
).total_seconds()
|
||
)
|
||
* 1e6
|
||
) + int(dt_obj.microsecond)
|
||
if ts == data:
|
||
success = True
|
||
elif precision == "ns":
|
||
if data == self.queryResult[row][col]:
|
||
success = True
|
||
if success:
|
||
if show:
|
||
tdLog.info("check successfully")
|
||
return True
|
||
else:
|
||
if exit:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (
|
||
caller.filename,
|
||
caller.lineno,
|
||
self.sql,
|
||
row,
|
||
col,
|
||
self.queryResult[row][col],
|
||
data,
|
||
)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s row:%d col:%d data:%s != expect:%s"
|
||
% args
|
||
)
|
||
else:
|
||
return False
|
||
elif isinstance(data, datetime.datetime):
|
||
dt_obj = self.queryResult[row][col]
|
||
delt_data = data - datetime.datetime.fromtimestamp(0, data.tzinfo)
|
||
delt_result = self.queryResult[row][
|
||
col
|
||
] - datetime.datetime.fromtimestamp(
|
||
0, self.queryResult[row][col].tzinfo
|
||
)
|
||
if delt_data == delt_result:
|
||
if show:
|
||
tdLog.info("check successfully")
|
||
return True
|
||
else:
|
||
if exit:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (
|
||
caller.filename,
|
||
caller.lineno,
|
||
self.sql,
|
||
row,
|
||
col,
|
||
self.queryResult[row][col],
|
||
data,
|
||
)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s row:%d col:%d data:%s != expect:%s"
|
||
% args
|
||
)
|
||
return False
|
||
else:
|
||
if exit:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (
|
||
caller.filename,
|
||
caller.lineno,
|
||
self.sql,
|
||
row,
|
||
col,
|
||
self.queryResult[row][col],
|
||
data,
|
||
)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s row:%d col:%d data:%s != expect:%s"
|
||
% args
|
||
)
|
||
else:
|
||
return False
|
||
|
||
if str(self.queryResult[row][col]) == str(data):
|
||
# tdLog.info(f"sql:{self.sql}, row:{row} col:{col} data:{self.queryResult[row][col]} == expect:{data}")
|
||
if show:
|
||
tdLog.info("check successfully")
|
||
return True
|
||
|
||
elif isinstance(data, float):
|
||
if (
|
||
abs(data) >= 1
|
||
and abs((self.queryResult[row][col] - data) / data) <= 0.000001
|
||
):
|
||
# tdLog.info(f"sql:{self.sql}, row:{row} col:{col} data:{self.queryResult[row][col]} == expect:{data}")
|
||
if show:
|
||
tdLog.info("check successfully")
|
||
elif (
|
||
abs(data) < 1 and abs(self.queryResult[row][col] - data) <= 0.000001
|
||
):
|
||
# tdLog.info(f"sql:{self.sql}, row:{row} col:{col} data:{self.queryResult[row][col]} == expect:{data}")
|
||
if show:
|
||
tdLog.info("check successfully")
|
||
|
||
else:
|
||
if exit:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (
|
||
caller.filename,
|
||
caller.lineno,
|
||
self.sql,
|
||
row,
|
||
col,
|
||
self.queryResult[row][col],
|
||
data,
|
||
)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s row:%d col:%d data:%s != expect:%s"
|
||
% args
|
||
)
|
||
else:
|
||
return False
|
||
return True
|
||
else:
|
||
if exit:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (
|
||
caller.filename,
|
||
caller.lineno,
|
||
self.sql,
|
||
row,
|
||
col,
|
||
self.queryResult[row][col],
|
||
data,
|
||
)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s row:%d col:%d data:%s != expect:%s"
|
||
% args
|
||
)
|
||
else:
|
||
return False
|
||
if show:
|
||
tdLog.info("check successfully")
|
||
return True
|
||
|
||
def checkDataV2(self, row, col, data, show=False, operator="==") -> bool:
|
||
"""
|
||
Compare the data at the specified row and column with the expected data.
|
||
|
||
Args:
|
||
row (int): The row index of the data to be checked.
|
||
col (int): The column index of the data to be checked.
|
||
data: The expected data to compare against.
|
||
show (bool, optional): If True, logs a message when the check is successful. Defaults to False.
|
||
operator (str, optional): The operator to use for comparison. Defaults to "==".
|
||
Returns:
|
||
bool: True if the comparison is successful, False otherwise.
|
||
Usage:
|
||
assert self.checkDataV2(row, col, data, show=True, operator="==")
|
||
assert self.checkDataV2(row, col, data, show=True, operator="<") # means actual value is less than data
|
||
"""
|
||
if row >= self.queryRows:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, row + 1, self.queryRows)
|
||
tdLog.error(
|
||
"%s(%d) failed: sql:%s, row:%d is larger than queryRows:%d" % args
|
||
)
|
||
return False
|
||
if col >= self.queryCols:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, col + 1, self.queryCols)
|
||
tdLog.error(
|
||
"%s(%d) failed: sql:%s, col:%d is larger than queryCols:%d" % args
|
||
)
|
||
return False
|
||
self.checkRowCol(row, col)
|
||
|
||
try:
|
||
# 获取当前单元格的数据
|
||
actual_value = self.queryResult[row][col]
|
||
|
||
# 转换函数:将值统一转换为可比较的类型
|
||
def to_timestamp(value) -> Optional[int]:
|
||
"""将任意时间格式转换为纳秒级时间戳"""
|
||
if value is None:
|
||
return None
|
||
|
||
# 处理数值时间戳
|
||
if isinstance(value, (int, float)):
|
||
if value > 1e18:
|
||
return int(value)
|
||
elif value > 1e15:
|
||
tdLog.info(f"value={value}")
|
||
tdLog.info(f"int(value * 1000)={int(value * 1000)}")
|
||
return int(value * 1000)
|
||
elif value > 1e12:
|
||
return int(value * 1000000)
|
||
else:
|
||
return int(value * 1000000000)
|
||
|
||
# 处理字符串时间
|
||
elif isinstance(value, str):
|
||
if value.isdigit():
|
||
if len(value) == 19:
|
||
return int(value)
|
||
elif len(value) == 16:
|
||
return int(value) * 1000
|
||
elif len(value) == 13:
|
||
return int(value) * 1000000
|
||
elif len(value) == 10:
|
||
return int(value) * 1000000000
|
||
try:
|
||
# 解析常规SQL格式(支持纳秒)
|
||
pattern = r"(\d{4}-\d{2}-\d{2})\s(\d{2}:\d{2}:\d{2})(\.\d+)?"
|
||
time_match = re.match(pattern, value)
|
||
if time_match:
|
||
date_part, time_part, nano_part = time_match.groups()
|
||
nano = int(
|
||
nano_part[1:].ljust(9, "0")[:9] if nano_part else 0
|
||
)
|
||
dt_str = f"{date_part or '1970-01-01'} {time_part}"
|
||
dt = datetime.datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S")
|
||
return int(dt.timestamp()) * 1000000000 + nano
|
||
except Exception:
|
||
pass
|
||
raise ValueError(f"无法解析的时间字符串: {value}")
|
||
|
||
# 处理datetime对象
|
||
elif isinstance(value, datetime.datetime):
|
||
return (
|
||
int(value.timestamp()) * 1000000000 + value.microsecond * 1000
|
||
)
|
||
|
||
raise TypeError(f"不支持的类型: {type(value)}")
|
||
|
||
# 转换实际值和预期值
|
||
if self.cursor.istype(col, "TIMESTAMP"):
|
||
converted_actual = to_timestamp(actual_value)
|
||
tdLog.debug(f"sql_result={converted_actual}")
|
||
converted_expected = to_timestamp(data)
|
||
tdLog.debug(f"sql_expect={converted_expected}")
|
||
else:
|
||
converted_actual = actual_value
|
||
converted_expected = data
|
||
|
||
# 处理None值比较
|
||
if converted_actual is None or converted_expected is None:
|
||
result = converted_actual == converted_expected
|
||
if operator != "==" and operator != "!=":
|
||
raise ValueError("None values can only be compared with == or !=")
|
||
result = result if operator == "==" else not result
|
||
else:
|
||
# 根据操作符进行比较
|
||
if operator == "<":
|
||
result = converted_actual < converted_expected
|
||
elif operator == "<=":
|
||
result = converted_actual <= converted_expected
|
||
elif operator == ">":
|
||
result = converted_actual > converted_expected
|
||
elif operator == ">=":
|
||
result = converted_actual >= converted_expected
|
||
elif operator == "==":
|
||
result = converted_actual == converted_expected
|
||
elif operator == "!=":
|
||
result = converted_actual != converted_expected
|
||
else:
|
||
raise ValueError(f"Unsupported operator: {operator}")
|
||
if show:
|
||
tdLog.info(
|
||
f"Data at row {row}, col {col} actual value={actual_value}, operator={operator}, expected value={data}, result={result}"
|
||
)
|
||
return result
|
||
except Exception as e:
|
||
tdLog.error(f"Error comparing data at row {row}, col {col}: {e}")
|
||
return False
|
||
|
||
def checkKeyData(self, key, col, data, show=False):
|
||
"""
|
||
Checks if the data at the specified key matches the expected data.
|
||
|
||
Args:
|
||
key: The first column to be compared with.
|
||
col (int): The column index of the data to be checked.
|
||
data: The expected data to be compared with.
|
||
show (bool, optional): If True, logs a message when the check is successful. Defaults to False.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
SystemExit: If the data of the specified key does not match the expected data.
|
||
"""
|
||
|
||
if col >= self.queryCols:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, col + 1, self.queryCols)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, col:%d is larger than queryCols:%d" % args
|
||
)
|
||
|
||
row = -1
|
||
|
||
for i in range(self.queryRows):
|
||
if self.queryResult[i][col] == data:
|
||
row = i
|
||
|
||
if show:
|
||
tdLog.info(f"find key:{key}, row:{row} col:{col}, data:{data}")
|
||
|
||
if row == -1:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, key, col)
|
||
tdLog.exit("%s(%d) failed: sql:%s key:%s col:%d not found" % args)
|
||
|
||
if show:
|
||
tdLog.info("check key successfully")
|
||
|
||
def checkKeyExist(self, key, show=False):
|
||
return self.checkKeyData(key, 0, key, show=show)
|
||
|
||
def printResult(self, name="results", exit=False, input_result=None, input_sql=""):
|
||
if input_result == None:
|
||
rows = self.queryRows
|
||
cols = self.queryCols
|
||
results = self.queryResult
|
||
else:
|
||
rows = len(input_result)
|
||
if rows == 0:
|
||
cols = 0
|
||
else:
|
||
cols = len(input_result[0])
|
||
results = input_result
|
||
|
||
if input_sql == "":
|
||
sql = self.sql
|
||
else:
|
||
sql = input_sql
|
||
|
||
tdLog.info(f"==== {name}, rows:{rows}, cols:{cols}, sql:{sql}")
|
||
for r in range(rows):
|
||
data = "==== "
|
||
for c in range(cols):
|
||
data += f"d[{r}][{c}]={results[r][c]} "
|
||
tdLog.info(data)
|
||
|
||
if exit:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
tdLog.info(f"{name} {caller.filename}({caller.lineno})")
|
||
caller = inspect.getframeinfo(inspect.stack()[2][0])
|
||
tdLog.exit(f"{name} {caller.filename}({caller.lineno})")
|
||
|
||
def expectKeyData(self, key, col, data, show=False):
|
||
"""
|
||
Whether the data at the specified key matches the expected data.
|
||
|
||
Args:
|
||
key: The first column to be compared with.
|
||
col (int): The column index of the data to be checked.
|
||
data: The expected data to be compared with.
|
||
show (bool, optional): If True, logs a message when the check is successful. Defaults to False.
|
||
|
||
Returns:
|
||
Bool
|
||
|
||
Raises:
|
||
None
|
||
"""
|
||
|
||
if col >= self.queryCols:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, col + 1, self.queryCols)
|
||
tdLog.info(
|
||
"%s(%d) failed: sql:%s, col:%d is larger than queryCols:%d" % args
|
||
)
|
||
return False
|
||
|
||
row = -1
|
||
|
||
for i in range(self.queryRows):
|
||
if self.queryResult[i][col] == data:
|
||
row = i
|
||
|
||
tdLog.info(f"find key:{key}, row:{row} col:{col}, data:{data}")
|
||
|
||
if row == -1:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, key, col)
|
||
return False
|
||
|
||
if show:
|
||
tdLog.info("check key successfully")
|
||
|
||
return True
|
||
|
||
def checkAssert(self, assertVal, show=False):
|
||
"""
|
||
Checks if the assertVal is true.
|
||
|
||
Args:
|
||
assertVal: The value to be assert
|
||
show (bool, optional): If True, logs a message when the check is successful. Defaults to False.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
SystemExit: If the data of the specified key does not match the expected data.
|
||
"""
|
||
|
||
if assertVal != True:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql)
|
||
tdLog.exit("%s(%d) failed: sql:%s asserted" % args)
|
||
|
||
if show:
|
||
tdLog.info("check assert successfully")
|
||
|
||
def checkDataMem(self, sql, mem):
|
||
"""
|
||
Executes a SQL query and checks if the result matches the expected data.
|
||
|
||
Args:
|
||
sql (str): The SQL query to be executed.
|
||
mem (list): The expected data, represented as a list of lists.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
SystemExit: If the expected data is not a list of lists, or if the SQL result does not match the expected data.
|
||
"""
|
||
self.query(sql)
|
||
if not isinstance(mem, list):
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, expect data is error, must is array[][]" % args
|
||
)
|
||
|
||
if len(mem) != self.queryRows:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, len(mem), self.queryRows)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, row:%d is larger than queryRows:%d" % args
|
||
)
|
||
# row, col, data
|
||
for row, rowData in enumerate(mem):
|
||
for col, colData in enumerate(rowData):
|
||
self.checkData(row, col, colData)
|
||
tdLog.info("check successfully")
|
||
|
||
def checkDataCsv(self, sql, csvfilePath):
|
||
"""
|
||
Executes a SQL query and checks if the result matches the expected data from a CSV file.(Not used)
|
||
|
||
Args:
|
||
sql (str): The SQL query to be executed.
|
||
csvfilePath (str): The path to the CSV file containing the expected data.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
SystemExit: If the CSV file path is invalid, the file is not found, there is an error reading the file,
|
||
or if the sql result does not match the expected data from CSV file.
|
||
"""
|
||
if not isinstance(csvfilePath, str) or len(csvfilePath) == 0:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, csvfilePath)
|
||
tdLog.exit("%s(%d) failed: sql:%s, expect csvfile path error:%s" % args)
|
||
|
||
tdLog.info("read csvfile read begin")
|
||
data = []
|
||
try:
|
||
with open(csvfilePath) as csvfile:
|
||
csv_reader = csv.reader(csvfile) # csv.reader read csvfile\
|
||
# header = next(csv_reader) # Read the header of each column in the first row
|
||
for row in csv_reader: # csv file save to data
|
||
data.append(row)
|
||
except FileNotFoundError:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, csvfilePath)
|
||
tdLog.exit("%s(%d) failed: sql:%s, expect csvfile not find error:%s" % args)
|
||
except Exception as e:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, csvfilePath, str(e))
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, expect csvfile path:%s, read error:%s" % args
|
||
)
|
||
|
||
tdLog.info("read csvfile read successfully")
|
||
self.checkDataMem(sql, data)
|
||
|
||
def checkDataMemByLine(self, sql, mem):
|
||
"""
|
||
Executes a SQL query and checks if the result matches the expected data (Same as checkDataMem).
|
||
|
||
Args:
|
||
sql (str): The SQL query to be executed.
|
||
mem (list): The expected data, represented as a list of lists.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
SystemExit: If the expected data is not a list of lists, or if the SQL result does not match the expected data.
|
||
"""
|
||
if not isinstance(mem, list):
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, expect data is error, must is array[][]" % args
|
||
)
|
||
|
||
if len(mem) != self.queryRows:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, len(mem), self.queryRows)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, row:%d is larger than queryRows:%d" % args
|
||
)
|
||
# row, col, data
|
||
for row, rowData in enumerate(mem):
|
||
for col, colData in enumerate(rowData):
|
||
self.checkData(row, col, colData)
|
||
tdLog.info("check %s successfully" % sql)
|
||
|
||
def checkDataCsvByLine(self, sql, csvfilePath):
|
||
"""
|
||
Executes a SQL query and checks if the result matches the expected data from a CSV file line by line.
|
||
|
||
Args:
|
||
sql (str): The SQL query to be executed.
|
||
csvfilePath (str): The path to the CSV file containing the expected data.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
SystemExit: If the CSV file path is invalid, the file is not found, there is an error reading the file,
|
||
or if the SQL result does not match the expected data from the CSV file.
|
||
"""
|
||
if not isinstance(csvfilePath, str) or len(csvfilePath) == 0:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, csvfilePath)
|
||
tdLog.exit("%s(%d) failed: sql:%s, expect csvfile path error:%s" % args)
|
||
self.query(sql)
|
||
data = []
|
||
tdLog.info("check line %d start" % self.csvLine)
|
||
try:
|
||
with open(csvfilePath) as csvfile:
|
||
skip_rows = self.csvLine
|
||
# 计算需要读取的行数
|
||
num_rows = self.queryRows
|
||
# 读取指定范围的行
|
||
df = pd.read_csv(
|
||
csvfilePath, skiprows=skip_rows, nrows=num_rows, header=None
|
||
)
|
||
for index, row in df.iterrows():
|
||
data.append(row)
|
||
self.csvLine += self.queryRows
|
||
except FileNotFoundError:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, csvfilePath)
|
||
tdLog.exit("%s(%d) failed: sql:%s, expect csvfile not find error:%s" % args)
|
||
except Exception as e:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, csvfilePath, str(e))
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, expect csvfile path:%s, read error:%s" % args
|
||
)
|
||
self.checkDataMemByLine(sql, data)
|
||
|
||
# return true or false replace exit, no print out
|
||
def checkRowColNoExist(self, row, col):
|
||
"""
|
||
Checks if the specified row and column indices are within the range of the last query result without exiting the program.
|
||
|
||
Args:
|
||
row (int): The row index to be checked.
|
||
col (int): The column index to be checked.
|
||
|
||
Returns:
|
||
bool: True if the specified row and column indices are within the range, otherwise False.
|
||
|
||
Raises:
|
||
None
|
||
"""
|
||
caller = inspect.getframeinfo(inspect.stack()[2][0])
|
||
if row < 0:
|
||
args = (caller.filename, caller.lineno, self.sql, row)
|
||
return False
|
||
if col < 0:
|
||
args = (caller.filename, caller.lineno, self.sql, row)
|
||
return False
|
||
if row > self.queryRows:
|
||
args = (caller.filename, caller.lineno, self.sql, row, self.queryRows)
|
||
return False
|
||
if col > self.queryCols:
|
||
args = (caller.filename, caller.lineno, self.sql, col, self.queryCols)
|
||
return False
|
||
|
||
return True
|
||
|
||
# return true or false replace exit, no print out
|
||
def checkDataNoExist(self, row, col, data):
|
||
"""
|
||
Checks if the data at the specified row and column matches the expected data without exiting the program.
|
||
|
||
Args:
|
||
row (int): The row index of the data to be checked.
|
||
col (int): The column index of the data to be checked.
|
||
data: The expected data to be compared with.
|
||
|
||
Returns:
|
||
bool: True if the data matches the expected data, otherwise False.
|
||
|
||
Raises:
|
||
None
|
||
"""
|
||
if self.checkRowColNoExist(row, col) == False:
|
||
return False
|
||
if self.queryResult[row][col] != data:
|
||
if self.cursor.istype(col, "TIMESTAMP"):
|
||
# suppose user want to check nanosecond timestamp if a longer data passed
|
||
if len(data) >= 28:
|
||
if pd.to_datetime(self.queryResult[row][col]) == pd.to_datetime(
|
||
data
|
||
):
|
||
return True
|
||
else:
|
||
if self.queryResult[row][col] == _parse_datetime(data):
|
||
return True
|
||
return False
|
||
|
||
if str(self.queryResult[row][col]) == str(data):
|
||
return True
|
||
elif isinstance(data, float):
|
||
if (
|
||
abs(data) >= 1
|
||
and abs((self.queryResult[row][col] - data) / data) <= 0.000001
|
||
):
|
||
return True
|
||
elif (
|
||
abs(data) < 1 and abs(self.queryResult[row][col] - data) <= 0.000001
|
||
):
|
||
return True
|
||
else:
|
||
return False
|
||
else:
|
||
return False
|
||
|
||
return True
|
||
|
||
# loop execute sql then sleep(waitTime) , if checkData ok break loop
|
||
def checkDataLoop(self, row, col, data, sql, loopCount, waitTime):
|
||
"""
|
||
Executes a SQL query in a loop and checks if the data at the specified row and column matches the expected data.
|
||
|
||
Args:
|
||
row (int): The row index of the data to be checked.
|
||
col (int): The column index of the data to be checked.
|
||
data: The expected data to be compared with.
|
||
sql (str): The SQL query to be executed.
|
||
loopCount (int): The number of times to execute the SQL query.
|
||
waitTime (int): The time to wait (in seconds) between each execution.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
Exception: If the query execution fails.
|
||
SystemExit: If the data at the specified row and column does not match the expected data.
|
||
"""
|
||
# loop check util checkData return true
|
||
for i in range(loopCount):
|
||
self.query(sql)
|
||
if self.checkDataNoExist(row, col, data):
|
||
self.checkData(row, col, data)
|
||
return
|
||
time.sleep(waitTime)
|
||
|
||
# last check
|
||
self.query(sql)
|
||
self.checkData(row, col, data)
|
||
|
||
def checkRowsLoop(self, expectedRows, sql, loopCount, waitTime):
|
||
# loop check util checkData return true
|
||
for i in range(loopCount):
|
||
self.query(sql)
|
||
if self.checkRowsNotExited(expectedRows):
|
||
return
|
||
else:
|
||
time.sleep(waitTime)
|
||
continue
|
||
# last check
|
||
self.query(sql)
|
||
self.checkRows(expectedRows)
|
||
|
||
def checkAffectedRows(self, expectAffectedRows):
|
||
"""
|
||
Checks if the number of affected rows from the last executed SQL statement matches the expected number of affected rows.
|
||
|
||
Args:
|
||
expectAffectedRows (int): The expected number of affected rows.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
SystemExit: If the number of affected rows does not match the expected number.
|
||
"""
|
||
if self.affectedRows != expectAffectedRows:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (
|
||
caller.filename,
|
||
caller.lineno,
|
||
self.sql,
|
||
self.affectedRows,
|
||
expectAffectedRows,
|
||
)
|
||
tdLog.exit("%s(%d) failed: sql:%s, affectedRows:%d != expect:%d" % args)
|
||
|
||
tdLog.info(
|
||
"sql:%s, affectedRows:%d == expect:%d"
|
||
% (self.sql, self.affectedRows, expectAffectedRows)
|
||
)
|
||
|
||
def checkColNameList(self, col_name_list, expect_col_name_list):
|
||
"""
|
||
Checks if the column names from the last query match the expected column names.
|
||
|
||
Args:
|
||
col_name_list (list): The list of column names from the last query.
|
||
expect_col_name_list (list): The list of expected column names.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
SystemExit: If the column names do not match the expected column names.
|
||
"""
|
||
if col_name_list == expect_col_name_list:
|
||
tdLog.info(
|
||
"sql:%s, col_name_list:%s == expect_col_name_list:%s"
|
||
% (self.sql, col_name_list, expect_col_name_list)
|
||
)
|
||
else:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (
|
||
caller.filename,
|
||
caller.lineno,
|
||
self.sql,
|
||
col_name_list,
|
||
expect_col_name_list,
|
||
)
|
||
tdLog.exit(
|
||
"%s(%d) failed: sql:%s, col_name_list:%s != expect_col_name_list:%s"
|
||
% args
|
||
)
|
||
|
||
def checkResColNameList(self, expect_col_name_list):
|
||
col_name_list = []
|
||
col_type_list = []
|
||
for query_col in self.cursor.description:
|
||
col_name_list.append(query_col[0])
|
||
col_type_list.append(query_col[1])
|
||
|
||
self.checkColNameList(col_name_list, expect_col_name_list)
|
||
|
||
def __check_equal(self, elm, expect_elm):
|
||
if elm == expect_elm:
|
||
return True
|
||
|
||
if isinstance(elm, datetime.datetime) and isinstance(expect_elm, str):
|
||
try:
|
||
parsed = datetime.datetime.fromisoformat(expect_elm)
|
||
return elm == parsed
|
||
except ValueError:
|
||
return False
|
||
if isinstance(expect_elm, datetime.datetime) and isinstance(elm, str):
|
||
try:
|
||
parsed = datetime.datetime.fromisoformat(elm)
|
||
return expect_elm == parsed
|
||
except ValueError:
|
||
return False
|
||
|
||
if type(elm) in (list, tuple) and type(expect_elm) in (list, tuple):
|
||
if len(elm) != len(expect_elm):
|
||
return False
|
||
if len(elm) == 0:
|
||
return True
|
||
for i in range(len(elm)):
|
||
flag = self.__check_equal(elm[i], expect_elm[i])
|
||
if not flag:
|
||
return False
|
||
return True
|
||
return False
|
||
|
||
def print_error_frame_info(self, elm, expect_elm, sql=None):
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
print_sql = self.sql if sql is None else sql
|
||
args = (caller.filename, caller.lineno, print_sql, elm, expect_elm)
|
||
# tdLog.info("%s(%d) failed: sql:%s, elm:%s != expect_elm:%s" % args)
|
||
raise Exception("%s(%d) failed: sql:%s, elm:%s != expect_elm:%s" % args)
|
||
|
||
def checkEqual(self, elm, expect_elm, show=False):
|
||
"""
|
||
Checks if the given element is equal to the expected element.
|
||
|
||
Args:
|
||
elm: The element to be checked.
|
||
expect_elm: The expected element to be compared with.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
Exception: If the element does not match the expected element.
|
||
"""
|
||
if elm == expect_elm:
|
||
if show:
|
||
tdLog.info(
|
||
"sql:%s, elm:%s == expect_elm:%s" % (self.sql, elm, expect_elm)
|
||
)
|
||
return True
|
||
if self.__check_equal(elm, expect_elm):
|
||
if show:
|
||
tdLog.info(
|
||
"sql:%s, elm:%s == expect_elm:%s" % (self.sql, elm, expect_elm)
|
||
)
|
||
return True
|
||
self.print_error_frame_info(elm, expect_elm)
|
||
|
||
def checkNotEqual(self, elm, expect_elm):
|
||
"""
|
||
Checks if the given element is not equal to the expected element.
|
||
|
||
Args:
|
||
elm: The element to be checked.
|
||
expect_elm: The expected element to be compared with.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
Exception: If the element matches the expected element.
|
||
"""
|
||
if elm != expect_elm:
|
||
tdLog.info("sql:%s, elm:%s != expect_elm:%s" % (self.sql, elm, expect_elm))
|
||
else:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, elm, expect_elm)
|
||
tdLog.info("%s(%d) failed: sql:%s, elm:%s == expect_elm:%s" % args)
|
||
raise Exception
|
||
|
||
def checkGreater(self, elm, expect_elm):
|
||
"""Verifies that the first element is greater than the second element.
|
||
|
||
This method compares two values and ensures that the first value (elm) is
|
||
strictly greater than the second value (expect_elm). It's commonly used for
|
||
validating query results, performance metrics, or any numeric comparisons
|
||
in test cases.
|
||
|
||
Args:
|
||
elm: The actual value to be compared. Can be any comparable type
|
||
(int, float, string, etc.).
|
||
expect_elm: The expected threshold value that elm should exceed.
|
||
Must be the same comparable type as elm.
|
||
|
||
Returns:
|
||
bool: True if elm > expect_elm, False otherwise.
|
||
"""
|
||
if elm > expect_elm:
|
||
tdLog.info("sql:%s, elm:%s > expect_elm:%s" % (self.sql, elm, expect_elm))
|
||
return True
|
||
else:
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
args = (caller.filename, caller.lineno, self.sql, elm, expect_elm)
|
||
tdLog.info("%s(%d) failed: sql:%s, elm:%s <= expect_elm:%s" % args)
|
||
self.print_error_frame_info(elm, expect_elm)
|
||
return False
|
||
|
||
# check like select count(*) ... sql
|
||
def checkAgg(self, sql, expectCnt):
|
||
"""
|
||
Executes an aggregate SQL query and checks if the result matches the expected count.
|
||
|
||
Args:
|
||
sql (str): The aggregate SQL query to be executed.
|
||
expectCnt (int): The expected count from the aggregate query.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
Exception: If the query execution fails.
|
||
SystemExit: If the result of the aggregate query does not match the expected count.
|
||
"""
|
||
self.query(sql)
|
||
self.checkData(0, 0, expectCnt)
|
||
tdLog.info(f"{sql} expect {expectCnt} ok.")
|
||
|
||
# expect first value
|
||
def checkFirstValue(self, sql, expect):
|
||
"""
|
||
Executes a SQL query and checks if the first value in the result matches the expected value.
|
||
|
||
Args:
|
||
sql (str): The SQL query to be executed.
|
||
expect: The expected value of the first result.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
Exception: If the query execution fails.
|
||
SystemExit: If the first value in the result does not match the expected value.
|
||
"""
|
||
self.query(sql)
|
||
self.checkData(0, 0, expect)
|
||
|
||
# colIdx1 value same with colIdx2
|
||
def checkSameColumn(self, c1, c2):
|
||
"""
|
||
Checks if the values in two specified columns are the same for all rows in the last query result.
|
||
|
||
Args:
|
||
c1 (int): The index of the first column to be checked.
|
||
c2 (int): The index of the second column to be checked.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
SystemExit: If the values in the specified columns are not the same for any row.
|
||
"""
|
||
for i in range(self.queryRows):
|
||
if self.queryResult[i][c1] != self.queryResult[i][c2]:
|
||
tdLog.exit(
|
||
f"Not same. row={i} col1={c1} col2={c2}. {self.queryResult[i][c1]}!={self.queryResult[i][c2]}"
|
||
)
|
||
tdLog.info(
|
||
f"check {self.queryRows} rows two column value same. column index [{c1},{c2}]"
|
||
)
|
||
|
||
#
|
||
# others session
|
||
#
|
||
|
||
def getTimes(self, time_str, precision="ms"):
|
||
"""
|
||
Converts a time string to a timestamp based on the specified precision.(Not used)
|
||
|
||
Args:
|
||
time_str (str): The time string to be converted. The string should end with a character indicating the time unit (e.g., 's' for seconds, 'm' for minutes).
|
||
precision (str, optional): The precision of the timestamp. Can be "ms" (milliseconds), "us" (microseconds), or "ns" (nanoseconds). Defaults to "ms".
|
||
|
||
Returns:
|
||
int: The timestamp in the specified precision.
|
||
|
||
Raises:
|
||
SystemExit: If the time string does not end with a valid time unit character or if the precision is not valid.
|
||
"""
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
if time_str[-1] not in TAOS_TIME_INIT:
|
||
tdLog.exit(
|
||
f"{caller.filename}({caller.lineno}) failed: {time_str} not a standard taos time init"
|
||
)
|
||
if precision not in TAOS_PRECISION:
|
||
tdLog.exit(
|
||
f"{caller.filename}({caller.lineno}) failed: {precision} not a standard taos time precision"
|
||
)
|
||
|
||
if time_str[-1] == TAOS_TIME_INIT[0]:
|
||
times = int(time_str[:-1]) * TIME_NS
|
||
if time_str[-1] == TAOS_TIME_INIT[1]:
|
||
times = int(time_str[:-1]) * TIME_US
|
||
if time_str[-1] == TAOS_TIME_INIT[2]:
|
||
times = int(time_str[:-1]) * TIME_MS
|
||
if time_str[-1] == TAOS_TIME_INIT[3]:
|
||
times = int(time_str[:-1]) * TIME_S
|
||
if time_str[-1] == TAOS_TIME_INIT[4]:
|
||
times = int(time_str[:-1]) * TIME_M
|
||
if time_str[-1] == TAOS_TIME_INIT[5]:
|
||
times = int(time_str[:-1]) * TIME_H
|
||
if time_str[-1] == TAOS_TIME_INIT[6]:
|
||
times = int(time_str[:-1]) * TIME_D
|
||
if time_str[-1] == TAOS_TIME_INIT[7]:
|
||
times = int(time_str[:-1]) * TIME_W
|
||
if time_str[-1] == TAOS_TIME_INIT[8]:
|
||
times = int(time_str[:-1]) * TIME_N
|
||
if time_str[-1] == TAOS_TIME_INIT[9]:
|
||
times = int(time_str[:-1]) * TIME_Y
|
||
|
||
if precision == "ms":
|
||
return int(times)
|
||
elif precision == "us":
|
||
return int(times * 1000)
|
||
elif precision == "ns":
|
||
return int(times * 1000 * 1000)
|
||
|
||
def getType(self, col):
|
||
"""
|
||
Retrieves the data type of the specified column in the last query result.(Not used)
|
||
|
||
Args:
|
||
col (int): The column index for which the data type is to be retrieved.
|
||
|
||
Returns:
|
||
str: The data type of the specified column.
|
||
|
||
Raises:
|
||
None
|
||
"""
|
||
if self.cursor.istype(col, "BOOL"):
|
||
return "BOOL"
|
||
if self.cursor.istype(col, "INT"):
|
||
return "INT"
|
||
if self.cursor.istype(col, "BIGINT"):
|
||
return "BIGINT"
|
||
if self.cursor.istype(col, "TINYINT"):
|
||
return "TINYINT"
|
||
if self.cursor.istype(col, "SMALLINT"):
|
||
return "SMALLINT"
|
||
if self.cursor.istype(col, "FLOAT"):
|
||
return "FLOAT"
|
||
if self.cursor.istype(col, "DOUBLE"):
|
||
return "DOUBLE"
|
||
if self.cursor.istype(col, "BINARY"):
|
||
return "BINARY"
|
||
if self.cursor.istype(col, "NCHAR"):
|
||
return "NCHAR"
|
||
if self.cursor.istype(col, "TIMESTAMP"):
|
||
return "TIMESTAMP"
|
||
if self.cursor.istype(col, "JSON"):
|
||
return "JSON"
|
||
if self.cursor.istype(col, "TINYINT UNSIGNED"):
|
||
return "TINYINT UNSIGNED"
|
||
if self.cursor.istype(col, "SMALLINT UNSIGNED"):
|
||
return "SMALLINT UNSIGNED"
|
||
if self.cursor.istype(col, "INT UNSIGNED"):
|
||
return "INT UNSIGNED"
|
||
if self.cursor.istype(col, "BIGINT UNSIGNED"):
|
||
return "BIGINT UNSIGNED"
|
||
|
||
def taosdStatus(self, state):
|
||
tdLog.sleep(5)
|
||
pstate = 0
|
||
for i in range(30):
|
||
pstate = 0
|
||
pl = psutil.pids()
|
||
for pid in pl:
|
||
try:
|
||
if psutil.Process(pid).name() == "taosd":
|
||
print("have already started")
|
||
pstate = 1
|
||
break
|
||
except psutil.NoSuchProcess:
|
||
pass
|
||
if pstate == state:
|
||
break
|
||
if state or pstate:
|
||
tdLog.sleep(1)
|
||
continue
|
||
pstate = 0
|
||
break
|
||
|
||
args = (pstate, state)
|
||
if pstate == state:
|
||
tdLog.info("taosd state is %d == expect:%d" % args)
|
||
else:
|
||
tdLog.exit("taosd state is %d != expect:%d" % args)
|
||
pass
|
||
|
||
def haveFile(self, dir, state):
|
||
if os.path.exists(dir) and os.path.isdir(dir):
|
||
if not os.listdir(dir):
|
||
if state:
|
||
tdLog.exit("dir: %s is empty, expect: not empty" % dir)
|
||
else:
|
||
tdLog.info("dir: %s is empty, expect: empty" % dir)
|
||
else:
|
||
if state:
|
||
tdLog.info("dir: %s is not empty, expect: not empty" % dir)
|
||
else:
|
||
tdLog.exit("dir: %s is not empty, expect: empty" % dir)
|
||
else:
|
||
tdLog.exit("dir: %s doesn't exist" % dir)
|
||
|
||
def createDir(self, dir):
|
||
if os.path.exists(dir):
|
||
shutil.rmtree(dir)
|
||
tdLog.info("dir: %s is removed" % dir)
|
||
os.makedirs(dir, 755)
|
||
tdLog.info("dir: %s is created" % dir)
|
||
pass
|
||
|
||
def getDbVgroups(self, db_name: str = "test") -> list:
|
||
db_vgroups_list = []
|
||
self.query(f"show {db_name}.vgroups")
|
||
for result in self.queryResult:
|
||
db_vgroups_list.append(result[0])
|
||
vgroup_nums = len(db_vgroups_list)
|
||
tdLog.debug(f"{db_name} has {vgroup_nums} vgroups :{db_vgroups_list}")
|
||
self.query("select * from information_schema.ins_vnodes")
|
||
return db_vgroups_list
|
||
|
||
def getCluseterDnodes(self) -> list:
|
||
cluset_dnodes_list = []
|
||
self.query("show dnodes")
|
||
for result in self.queryResult:
|
||
cluset_dnodes_list.append(result[0])
|
||
self.clust_dnode_nums = len(cluset_dnodes_list)
|
||
tdLog.debug(
|
||
f"cluster has {len(cluset_dnodes_list)} dnodes :{cluset_dnodes_list}"
|
||
)
|
||
return cluset_dnodes_list
|
||
|
||
def redistribute_one_vgroup(
|
||
self,
|
||
db_name: str = "test",
|
||
replica: int = 1,
|
||
vgroup_id: int = 1,
|
||
useful_trans_dnodes_list: list = [],
|
||
):
|
||
# redisutribute vgroup {vgroup_id} dnode {dnode_id}
|
||
if replica == 1:
|
||
dnode_id = random.choice(useful_trans_dnodes_list)
|
||
redistribute_sql = f"redistribute vgroup {vgroup_id} dnode {dnode_id}"
|
||
elif replica == 3:
|
||
selected_dnodes = random.sample(useful_trans_dnodes_list, replica)
|
||
redistribute_sql_parts = [f"dnode {dnode}" for dnode in selected_dnodes]
|
||
redistribute_sql = f"redistribute vgroup {vgroup_id} " + " ".join(
|
||
redistribute_sql_parts
|
||
)
|
||
else:
|
||
raise ValueError(f"Replica count must be 1 or 3,but got {replica}")
|
||
tdLog.debug(f"redistributeSql:{redistribute_sql}")
|
||
self.query(redistribute_sql)
|
||
tdLog.debug("redistributeSql ok")
|
||
|
||
def redistributeDbAllVgroups(self, db_name: str = "test", replica: int = 1):
|
||
db_vgroups_list = self.getDbVgroups(db_name)
|
||
cluset_dnodes_list = self.getCluseterDnodes()
|
||
useful_trans_dnodes_list = cluset_dnodes_list.copy()
|
||
self.query("select * from information_schema.ins_vnodes")
|
||
# result: dnode_id|vgroup_id|db_name|status|role_time|start_time|restored|
|
||
|
||
results = list(self.queryResult)
|
||
for vnode_group_id in db_vgroups_list:
|
||
for result in results:
|
||
print(
|
||
f"result[2] is {result[2]}, db_name is {db_name}, result[1] is {result[1]}, vnode_group_id is {vnode_group_id}"
|
||
)
|
||
if result[2] == db_name and result[1] == vnode_group_id:
|
||
tdLog.debug(
|
||
f"dbname: {db_name}, vgroup :{vnode_group_id}, dnode is {result[0]}"
|
||
)
|
||
print(useful_trans_dnodes_list)
|
||
useful_trans_dnodes_list.remove(result[0])
|
||
tdLog.debug(
|
||
f"vgroup :{vnode_group_id},redis_dnode list:{useful_trans_dnodes_list}"
|
||
)
|
||
self.redistribute_one_vgroup(
|
||
db_name, replica, vnode_group_id, useful_trans_dnodes_list
|
||
)
|
||
useful_trans_dnodes_list = cluset_dnodes_list.copy()
|
||
|
||
def pause(self):
|
||
"""
|
||
Pause the execution of the program and wait for enter key. Used for debugging.
|
||
Args:
|
||
None
|
||
Returns:
|
||
None
|
||
Raises:
|
||
None
|
||
"""
|
||
if os.name == "nt": # Windows
|
||
os.system("pause") # 显示 "按任意键继续..."
|
||
else: # Linux/macOS
|
||
input("press enter to continue...")
|
||
|
||
def setConnMode(self, mode=1):
|
||
"""
|
||
Set Conn Mode
|
||
|
||
Args:
|
||
mode (int, optional): connect mode options.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
None
|
||
|
||
"""
|
||
tdLog.info(f"set connection mode:{mode}")
|
||
|
||
def checkResultsByFunc(self, sql, func, delay=0.0, retry=60, show=False):
|
||
if delay != 0:
|
||
time.sleep(delay)
|
||
|
||
# show sql
|
||
tdLog.info(sql)
|
||
|
||
if retry <= 0:
|
||
retry = 1
|
||
|
||
for loop in range(retry):
|
||
self.clearResult()
|
||
if self.query(sql, queryTimes=1, exit=False):
|
||
if func():
|
||
self.printResult(f"check succeed in {loop} seconds")
|
||
return
|
||
|
||
if loop != retry - 1:
|
||
if show:
|
||
self.printResult(f"check continue {loop} after sleep 1s ...")
|
||
time.sleep(1)
|
||
|
||
self.printResult(f"check failed for {retry} seconds", exit=True)
|
||
|
||
def checkResultsByArray(
|
||
self, sql, exp_result, exp_sql="", delay=0.0, retry=60, show=False
|
||
):
|
||
if delay != 0:
|
||
time.sleep(delay)
|
||
|
||
if retry <= 0:
|
||
retry = 1
|
||
|
||
for loop in range(retry):
|
||
self.clearResult()
|
||
res_result = self.getResult(sql, exit=False)
|
||
if res_result != []:
|
||
if self.compareResults(res_result, exp_result):
|
||
self.printResult(
|
||
f"check succeed in {loop} seconds", input_result=res_result
|
||
)
|
||
return
|
||
|
||
if loop != retry - 1:
|
||
if show:
|
||
self.printResult("check continue", input_result=res_result)
|
||
time.sleep(1)
|
||
|
||
self.printResult(f"expect results", input_result=exp_result, input_sql=exp_sql)
|
||
self.printResult(
|
||
f"check failed for {retry} seconds", input_result=res_result, input_sql=sql
|
||
)
|
||
self.compareResults(res_result, exp_result, show=True)
|
||
|
||
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
||
tdLog.info(f"{caller.filename}(caller.lineno) check result failed")
|
||
caller = inspect.getframeinfo(inspect.stack()[2][0])
|
||
tdLog.exit(f"{caller.filename}(caller.lineno) check result failed")
|
||
|
||
def checkResultsBySql(self, sql, exp_sql, delay=0.0, retry=60, show=False):
|
||
# sleep
|
||
if delay != 0:
|
||
time.sleep(delay)
|
||
|
||
# show sql
|
||
tdLog.info(sql)
|
||
|
||
if retry <= 0:
|
||
retry = 1
|
||
|
||
# loop retry
|
||
for loop in range(retry):
|
||
# clear
|
||
self.clearResult()
|
||
# query
|
||
exp_result = self.getResult(exp_sql, exit=False)
|
||
# check result
|
||
if exp_result != [] and exp_result != None:
|
||
# success
|
||
break
|
||
# sleep and retry
|
||
if loop != retry - 1:
|
||
if show:
|
||
self.printResult(f"check continue {loop} after sleep 1s ...")
|
||
time.sleep(1)
|
||
|
||
self.checkResultsByArray(sql, exp_result, exp_sql, delay, retry, show)
|
||
|
||
def checkTableType(
|
||
self,
|
||
dbname,
|
||
columns,
|
||
tags=0,
|
||
stbname=None,
|
||
tbname=None,
|
||
typename=None,
|
||
delay=0.0,
|
||
retry=60,
|
||
show=False,
|
||
):
|
||
if tags == 0:
|
||
sql = f"select * from information_schema.ins_tables where db_name='{dbname}' and table_name='{tbname}'"
|
||
self.checkResultsByFunc(
|
||
sql=sql,
|
||
func=lambda: self.getRows() == 1
|
||
and self.compareData(0, 0, tbname)
|
||
and self.compareData(0, 1, dbname)
|
||
and self.compareData(0, 3, columns)
|
||
and self.compareData(0, 4, stbname)
|
||
and self.compareData(0, 9, typename),
|
||
delay=delay,
|
||
retry=retry,
|
||
show=show,
|
||
)
|
||
else:
|
||
sql = f"select * from information_schema.ins_stables where db_name='{dbname}' and stable_name='{stbname}'"
|
||
self.checkResultsByFunc(
|
||
sql=sql,
|
||
func=lambda: self.getRows() == 1
|
||
and self.compareData(0, 0, stbname)
|
||
and self.compareData(0, 1, dbname)
|
||
and self.compareData(0, 3, columns)
|
||
and self.compareData(0, 4, tags),
|
||
delay=delay,
|
||
retry=retry,
|
||
show=show,
|
||
)
|
||
|
||
def checkTableSchema(
|
||
self,
|
||
dbname,
|
||
tbname,
|
||
schema,
|
||
delay=0.0,
|
||
retry=60,
|
||
show=False,
|
||
):
|
||
sql = f"desc {dbname}.{tbname}"
|
||
self.checkResultsByFunc(
|
||
sql=sql,
|
||
func=lambda: self.compareSchema(schema),
|
||
delay=delay,
|
||
retry=retry,
|
||
show=show,
|
||
)
|
||
|
||
def compareSchema(self, schema):
|
||
row = len(schema)
|
||
for r in range(row):
|
||
if (
|
||
schema[r][0] != self.queryResult[r][0] # field
|
||
or schema[r][1] != self.queryResult[r][1] # type
|
||
or schema[r][2] != self.queryResult[r][2] # length
|
||
or schema[r][3] != self.queryResult[r][3] # note
|
||
):
|
||
tdLog.info(f"exp_schema[{r}]={schema[r]}, res_schema[{r}]={self.queryResult[r]}")
|
||
return False
|
||
|
||
return True
|
||
|
||
def compareResults(self, res_result, exp_result, show=False):
|
||
exp_rows = len(exp_result)
|
||
res_rows = len(res_result)
|
||
|
||
if exp_rows != res_rows:
|
||
if show:
|
||
tdLog.info(f"exp_rows={exp_rows}, res_rows={res_rows}")
|
||
return False
|
||
if exp_rows == 0:
|
||
return True
|
||
|
||
exp_cols = len(exp_result[0])
|
||
res_cols = len(res_result[0])
|
||
if exp_cols != res_cols:
|
||
if show:
|
||
tdLog.info(f"exp_cols={exp_cols}, res_cols={res_cols}")
|
||
return False
|
||
if exp_cols == 0:
|
||
return True
|
||
|
||
for r in range(res_rows):
|
||
for c in range(res_cols):
|
||
if res_result[r][c] != exp_result[r][c]:
|
||
if show:
|
||
tdLog.info(
|
||
f"res_result[{r}][{c}]={res_result[r][c]}, exp_result[{r}][{c}]={exp_result[r][c]}"
|
||
)
|
||
return False
|
||
|
||
return True
|
||
|
||
def clearResult(self):
|
||
self.queryCols = 0
|
||
self.queryRows = 0
|
||
self.queryResult = []
|
||
|
||
|
||
# insert table with fixed values, return next write ts
|
||
def insertFixedVal(self, table, startTs, step, count, cols, fixedVals):
|
||
# init
|
||
ts = startTs
|
||
# loop count
|
||
for i in range(count):
|
||
sql = f"INSERT INTO {table}({cols}) VALUES({ts},{fixedVals})"
|
||
self.execute(sql, show=True)
|
||
# next
|
||
ts += step
|
||
|
||
return ts
|
||
|
||
|
||
# insert now
|
||
def insertNow(self, table, sleepS, count, cols, fixedVals):
|
||
# loop count
|
||
for i in range(count):
|
||
sql = f"INSERT INTO {table}({cols}) VALUES(now,{fixedVals})"
|
||
self.execute(sql, show=True)
|
||
# next
|
||
time.sleep(sleepS)
|
||
|
||
|
||
# insert table with order values, only support number cols, return next write ts
|
||
def insertOrderVal(self, table, startTs, step, count, cols, orderVals, colStep = 1):
|
||
# init
|
||
ts = startTs
|
||
colsVal = orderVals
|
||
|
||
# loop count
|
||
for i in range(count):
|
||
# insert sql
|
||
sql = f"INSERT INTO {table}({cols}) VALUES({ts}, {','.join(map(str, colsVal))})"
|
||
self.execute(sql, show=True)
|
||
# next
|
||
ts += step
|
||
for j in range(len(colsVal)):
|
||
colsVal[j] += colStep
|
||
|
||
return ts
|
||
|
||
# flush db
|
||
def flushDb(self, dbName):
|
||
self.execute(f"flush database {dbName}", show=True)
|
||
|
||
# delete rows
|
||
def deleteRows(self, table, where=None):
|
||
"""
|
||
Deletes rows from the specified table based on the given condition.
|
||
|
||
Args:
|
||
table (str): The name of the table from which rows are to be deleted.
|
||
where (str, optional): The condition for deleting rows. Defaults to None.
|
||
|
||
Returns:
|
||
None
|
||
|
||
Raises:
|
||
SystemExit: If the delete operation fails.
|
||
"""
|
||
sql = f"DELETE FROM {table}"
|
||
if where:
|
||
sql += f" WHERE {where}"
|
||
self.execute(sql, show=True)
|
||
|
||
|
||
# global
|
||
tdSql = TDSql()
|