str_func_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. // Copyright 2021-2022 EMQ Technologies Co., Ltd.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package operator
  15. import (
  16. "fmt"
  17. "reflect"
  18. "strings"
  19. "testing"
  20. "github.com/lf-edge/ekuiper/internal/conf"
  21. "github.com/lf-edge/ekuiper/internal/topo/context"
  22. "github.com/lf-edge/ekuiper/internal/xsql"
  23. "github.com/lf-edge/ekuiper/pkg/cast"
  24. )
  25. func TestStrFunc_Apply1(t *testing.T) {
  26. tests := []struct {
  27. sql string
  28. data *xsql.Tuple
  29. result []map[string]interface{}
  30. }{
  31. {
  32. sql: "SELECT concat(a, b, c) AS a FROM test",
  33. data: &xsql.Tuple{
  34. Emitter: "test",
  35. Message: xsql.Message{
  36. "a": "mya",
  37. "b": "myb",
  38. "c": "myc",
  39. },
  40. },
  41. result: []map[string]interface{}{{
  42. "a": "myamybmyc",
  43. }},
  44. },
  45. {
  46. sql: "SELECT concat(a, d, b, c) AS a FROM test",
  47. data: &xsql.Tuple{
  48. Emitter: "test",
  49. Message: xsql.Message{
  50. "a": "mya",
  51. "b": "myb",
  52. "c": "myc",
  53. },
  54. },
  55. result: []map[string]interface{}{{
  56. "a": "myamybmyc",
  57. }},
  58. },
  59. {
  60. sql: "SELECT endswith(a, b) AS a FROM test",
  61. data: &xsql.Tuple{
  62. Emitter: "test",
  63. Message: xsql.Message{
  64. "a": "mya",
  65. "b": "myb",
  66. "c": "myc",
  67. },
  68. },
  69. result: []map[string]interface{}{{
  70. "a": false,
  71. }},
  72. },
  73. {
  74. sql: "SELECT endswith(a, b) AS a FROM test",
  75. data: &xsql.Tuple{
  76. Emitter: "test",
  77. Message: xsql.Message{
  78. "a": "mya",
  79. "b": "ya",
  80. "c": "myc",
  81. },
  82. },
  83. result: []map[string]interface{}{{
  84. "a": true,
  85. }},
  86. },
  87. {
  88. sql: "SELECT endswith(a, d) AS a FROM test",
  89. data: &xsql.Tuple{
  90. Emitter: "test",
  91. Message: xsql.Message{
  92. "a": "mya",
  93. "b": "ya",
  94. "c": "myc",
  95. },
  96. },
  97. result: []map[string]interface{}{{
  98. "a": false,
  99. }},
  100. },
  101. {
  102. sql: "SELECT format_time(a, \"yyyy-MM-dd T HH:mm:ss\") AS a FROM test",
  103. data: &xsql.Tuple{
  104. Emitter: "test",
  105. Message: xsql.Message{
  106. "a": cast.TimeFromUnixMilli(1568854515000),
  107. "b": "ya",
  108. "c": "myc",
  109. },
  110. },
  111. result: []map[string]interface{}{{
  112. "a": "2019-09-19 T 00:55:15",
  113. }},
  114. },
  115. {
  116. sql: "SELECT format_time(meta(created) * 1000, \"yyyy-MM-dd T HH:mm:ss\") AS time FROM test",
  117. data: &xsql.Tuple{
  118. Emitter: "test",
  119. Message: xsql.Message{
  120. "a": "hello",
  121. "b": "ya",
  122. "c": "myc",
  123. },
  124. Metadata: xsql.Metadata{
  125. "created": 1.62000273e+09,
  126. },
  127. },
  128. result: []map[string]interface{}{{
  129. "time": "2021-05-03 T 00:45:30",
  130. }},
  131. },
  132. {
  133. sql: "SELECT format_time(d, \"yyyy-MM-dd T HH:mm:ss\") AS time FROM test",
  134. data: &xsql.Tuple{
  135. Emitter: "test",
  136. Message: xsql.Message{
  137. "a": "hello",
  138. "b": "ya",
  139. "c": "myc",
  140. },
  141. },
  142. result: []map[string]interface{}{{}},
  143. },
  144. {
  145. sql: "SELECT indexof(a, \"a\") AS a FROM test",
  146. data: &xsql.Tuple{
  147. Emitter: "test",
  148. Message: xsql.Message{
  149. "a": "mya",
  150. "b": "ya",
  151. "c": "myc",
  152. },
  153. },
  154. result: []map[string]interface{}{{
  155. "a": 2,
  156. }},
  157. },
  158. {
  159. sql: "SELECT indexof(d, \"a\") AS a FROM test",
  160. data: &xsql.Tuple{
  161. Emitter: "test",
  162. Message: xsql.Message{
  163. "a": "mya",
  164. "b": "ya",
  165. "c": "myc",
  166. },
  167. },
  168. result: []map[string]interface{}{{
  169. "a": -1,
  170. }},
  171. },
  172. {
  173. sql: "SELECT length(a) AS a FROM test",
  174. data: &xsql.Tuple{
  175. Emitter: "test",
  176. Message: xsql.Message{
  177. "a": "中国",
  178. "b": "ya",
  179. "c": "myc",
  180. },
  181. },
  182. result: []map[string]interface{}{{
  183. "a": 2,
  184. }},
  185. },
  186. {
  187. sql: "SELECT length(c) AS a FROM test",
  188. data: &xsql.Tuple{
  189. Emitter: "test",
  190. Message: xsql.Message{
  191. "a": "中国",
  192. "b": "ya",
  193. "c": "myc",
  194. },
  195. },
  196. result: []map[string]interface{}{{
  197. "a": 3,
  198. }},
  199. },
  200. {
  201. sql: "SELECT length(d) AS a FROM test",
  202. data: &xsql.Tuple{
  203. Emitter: "test",
  204. Message: xsql.Message{
  205. "a": "中国",
  206. "b": "ya",
  207. "c": "myc",
  208. },
  209. },
  210. result: []map[string]interface{}{{
  211. "a": 0,
  212. }},
  213. },
  214. {
  215. sql: "SELECT lower(a) AS a FROM test",
  216. data: &xsql.Tuple{
  217. Emitter: "test",
  218. Message: xsql.Message{
  219. "a": "NYCNicks",
  220. "b": "ya",
  221. "c": "myc",
  222. },
  223. },
  224. result: []map[string]interface{}{{
  225. "a": "nycnicks",
  226. }},
  227. },
  228. {
  229. sql: "SELECT lower(d) AS a FROM test",
  230. data: &xsql.Tuple{
  231. Emitter: "test",
  232. Message: xsql.Message{
  233. "a": "NYCNicks",
  234. "b": "ya",
  235. "c": "myc",
  236. },
  237. },
  238. result: []map[string]interface{}{{}},
  239. },
  240. {
  241. sql: "SELECT lpad(a, 2) AS a FROM test",
  242. data: &xsql.Tuple{
  243. Emitter: "test",
  244. Message: xsql.Message{
  245. "a": "NYCNicks",
  246. "b": "ya",
  247. "c": "myc",
  248. },
  249. },
  250. result: []map[string]interface{}{{
  251. "a": " NYCNicks",
  252. }},
  253. },
  254. {
  255. sql: "SELECT ltrim(a) AS a FROM test",
  256. data: &xsql.Tuple{
  257. Emitter: "test",
  258. Message: xsql.Message{
  259. "a": " \ttrimme\n ",
  260. "b": "ya",
  261. "c": "myc",
  262. },
  263. },
  264. result: []map[string]interface{}{{
  265. "a": "trimme\n ",
  266. }},
  267. },
  268. {
  269. sql: "SELECT numbytes(a) AS a FROM test",
  270. data: &xsql.Tuple{
  271. Emitter: "test",
  272. Message: xsql.Message{
  273. "a": "中国",
  274. "b": "ya",
  275. "c": "myc",
  276. },
  277. },
  278. result: []map[string]interface{}{{
  279. "a": 6,
  280. }},
  281. },
  282. {
  283. sql: "SELECT numbytes(b) AS a FROM test",
  284. data: &xsql.Tuple{
  285. Emitter: "test",
  286. Message: xsql.Message{
  287. "a": "中国",
  288. "b": "ya",
  289. "c": "myc",
  290. },
  291. },
  292. result: []map[string]interface{}{{
  293. "a": 2,
  294. }},
  295. },
  296. {
  297. sql: "SELECT regexp_matches(a,\"foo.*\") AS a FROM test",
  298. data: &xsql.Tuple{
  299. Emitter: "test",
  300. Message: xsql.Message{
  301. "a": "seafood",
  302. "b": "ya",
  303. "c": "myc",
  304. },
  305. },
  306. result: []map[string]interface{}{{
  307. "a": true,
  308. }},
  309. },
  310. {
  311. sql: "SELECT regexp_matches(b,\"foo.*\") AS a FROM test",
  312. data: &xsql.Tuple{
  313. Emitter: "test",
  314. Message: xsql.Message{
  315. "a": "seafood",
  316. "b": "ya",
  317. "c": "myc",
  318. },
  319. },
  320. result: []map[string]interface{}{{
  321. "a": false,
  322. }},
  323. },
  324. {
  325. sql: "SELECT regexp_matches(d,\"foo.*\") AS a FROM test",
  326. data: &xsql.Tuple{
  327. Emitter: "test",
  328. Message: xsql.Message{
  329. "a": "seafood",
  330. "b": "ya",
  331. "c": "myc",
  332. },
  333. },
  334. result: []map[string]interface{}{{
  335. "a": false,
  336. }},
  337. },
  338. {
  339. sql: "SELECT regexp_replace(a,\"a(x*)b\", \"REP\") AS a FROM test",
  340. data: &xsql.Tuple{
  341. Emitter: "test",
  342. Message: xsql.Message{
  343. "a": "-ab-axxb-",
  344. "b": "ya",
  345. "c": "myc",
  346. },
  347. },
  348. result: []map[string]interface{}{{
  349. "a": "-REP-REP-",
  350. }},
  351. },
  352. {
  353. sql: "SELECT regexp_replace(a,\"a(x*)b\", d) AS a FROM test",
  354. data: &xsql.Tuple{
  355. Emitter: "test",
  356. Message: xsql.Message{
  357. "a": "-ab-axxb-",
  358. "b": "ya",
  359. "c": "myc",
  360. },
  361. },
  362. result: []map[string]interface{}{{}},
  363. },
  364. {
  365. sql: "SELECT regexp_substr(a,\"foo.*\") AS a FROM test",
  366. data: &xsql.Tuple{
  367. Emitter: "test",
  368. Message: xsql.Message{
  369. "a": "seafood",
  370. "b": "ya",
  371. "c": "myc",
  372. },
  373. },
  374. result: []map[string]interface{}{{
  375. "a": "food",
  376. }},
  377. },
  378. {
  379. sql: "SELECT regexp_substr(d,\"foo.*\") AS a FROM test",
  380. data: &xsql.Tuple{
  381. Emitter: "test",
  382. Message: xsql.Message{
  383. "a": "seafood",
  384. "b": "ya",
  385. "c": "myc",
  386. },
  387. },
  388. result: []map[string]interface{}{{}},
  389. },
  390. {
  391. sql: "SELECT rpad(a, 3) AS a FROM test",
  392. data: &xsql.Tuple{
  393. Emitter: "test",
  394. Message: xsql.Message{
  395. "a": "NYCNicks",
  396. "b": "ya",
  397. "c": "myc",
  398. },
  399. },
  400. result: []map[string]interface{}{{
  401. "a": "NYCNicks ",
  402. }},
  403. },
  404. {
  405. sql: "SELECT rtrim(a) AS a FROM test",
  406. data: &xsql.Tuple{
  407. Emitter: "test",
  408. Message: xsql.Message{
  409. "a": " \ttrimme\n ",
  410. "b": "ya",
  411. "c": "myc",
  412. },
  413. },
  414. result: []map[string]interface{}{{
  415. "a": " \ttrimme",
  416. }},
  417. },
  418. {
  419. sql: "SELECT substring(a, 3) AS a FROM test",
  420. data: &xsql.Tuple{
  421. Emitter: "test",
  422. Message: xsql.Message{
  423. "a": "NYCNicks",
  424. "b": "ya",
  425. "c": "myc",
  426. },
  427. },
  428. result: []map[string]interface{}{{
  429. "a": "Nicks",
  430. }},
  431. },
  432. {
  433. sql: "SELECT substring(a, 3, 5) AS a FROM test",
  434. data: &xsql.Tuple{
  435. Emitter: "test",
  436. Message: xsql.Message{
  437. "a": "NYCNicks",
  438. "b": "ya",
  439. "c": "myc",
  440. },
  441. },
  442. result: []map[string]interface{}{{
  443. "a": "Ni",
  444. }},
  445. },
  446. {
  447. sql: "SELECT substring(a, 3, 100) AS a FROM test",
  448. data: &xsql.Tuple{
  449. Emitter: "test",
  450. Message: xsql.Message{
  451. "a": "NYCNicks",
  452. "b": "ya",
  453. "c": "myc",
  454. },
  455. },
  456. result: []map[string]interface{}{{
  457. "a": "Nicks",
  458. }},
  459. },
  460. {
  461. sql: "SELECT substring(a, 88, 100) AS a FROM test",
  462. data: &xsql.Tuple{
  463. Emitter: "test",
  464. Message: xsql.Message{
  465. "a": "NYCNicks",
  466. "b": "ya",
  467. "c": "myc",
  468. },
  469. },
  470. result: []map[string]interface{}{{
  471. "a": "",
  472. }},
  473. },
  474. {
  475. sql: "SELECT substring(a, 100) AS a FROM test",
  476. data: &xsql.Tuple{
  477. Emitter: "test",
  478. Message: xsql.Message{
  479. "a": "NYCNicks",
  480. "b": "ya",
  481. "c": "myc",
  482. },
  483. },
  484. result: []map[string]interface{}{{
  485. "a": "",
  486. }},
  487. },
  488. {
  489. sql: "SELECT substring(a, 100) AS a FROM test",
  490. data: &xsql.Tuple{
  491. Emitter: "test",
  492. Message: xsql.Message{
  493. "a": "NYCNicks",
  494. "b": "ya",
  495. "c": "myc",
  496. },
  497. },
  498. result: []map[string]interface{}{{
  499. "a": "",
  500. }},
  501. },
  502. {
  503. sql: "SELECT substring(d, 3, 100) AS bc FROM test",
  504. data: &xsql.Tuple{
  505. Emitter: "test",
  506. Message: xsql.Message{
  507. "a": "NYCNicks",
  508. "b": "ya",
  509. "c": "myc",
  510. },
  511. },
  512. result: []map[string]interface{}{{}},
  513. },
  514. {
  515. sql: "SELECT endswith(a, b) AS a FROM test",
  516. data: &xsql.Tuple{
  517. Emitter: "test",
  518. Message: xsql.Message{
  519. "a": "mya",
  520. "b": "ya",
  521. "c": "myc",
  522. },
  523. },
  524. result: []map[string]interface{}{{
  525. "a": true,
  526. }},
  527. },
  528. {
  529. sql: "SELECT endswith(a, c) AS a FROM test",
  530. data: &xsql.Tuple{
  531. Emitter: "test",
  532. Message: xsql.Message{
  533. "a": "mya",
  534. "b": "ya",
  535. "c": "myc",
  536. },
  537. },
  538. result: []map[string]interface{}{{
  539. "a": false,
  540. }},
  541. },
  542. {
  543. sql: "SELECT endswith(d, c) AS a FROM test",
  544. data: &xsql.Tuple{
  545. Emitter: "test",
  546. Message: xsql.Message{
  547. "a": "mya",
  548. "b": "ya",
  549. "c": "myc",
  550. },
  551. },
  552. result: []map[string]interface{}{{
  553. "a": false,
  554. }},
  555. },
  556. {
  557. sql: "SELECT trim(a) AS a FROM test",
  558. data: &xsql.Tuple{
  559. Emitter: "test",
  560. Message: xsql.Message{
  561. "a": " \ttrimme\n ",
  562. "b": "ya",
  563. "c": "myc",
  564. },
  565. },
  566. result: []map[string]interface{}{{
  567. "a": "trimme",
  568. }},
  569. },
  570. {
  571. sql: "SELECT upper(a) AS a FROM test",
  572. data: &xsql.Tuple{
  573. Emitter: "test",
  574. Message: xsql.Message{
  575. "a": "NYCNicks",
  576. "b": "ya",
  577. "c": "myc",
  578. },
  579. },
  580. result: []map[string]interface{}{{
  581. "a": "NYCNICKS",
  582. }},
  583. },
  584. {
  585. sql: `SELECT split_value(a,"/",0) AS a FROM test1`,
  586. data: &xsql.Tuple{
  587. Emitter: "test",
  588. Message: xsql.Message{
  589. "a": "test/device001/message",
  590. },
  591. },
  592. result: []map[string]interface{}{{
  593. "a": "test",
  594. }},
  595. },
  596. {
  597. sql: `SELECT split_value(a,"/",1) AS a FROM test1`,
  598. data: &xsql.Tuple{
  599. Emitter: "test",
  600. Message: xsql.Message{
  601. "a": "test/device001/message",
  602. },
  603. },
  604. result: []map[string]interface{}{{
  605. "a": "device001",
  606. }},
  607. },
  608. {
  609. sql: `SELECT split_value(a,"/",2) AS a FROM test1`,
  610. data: &xsql.Tuple{
  611. Emitter: "test",
  612. Message: xsql.Message{
  613. "a": "test/device001/message",
  614. },
  615. },
  616. result: []map[string]interface{}{{
  617. "a": "message",
  618. }},
  619. },
  620. {
  621. sql: `SELECT split_value(d,"/",2) AS a FROM test1`,
  622. data: &xsql.Tuple{
  623. Emitter: "test",
  624. Message: xsql.Message{
  625. "a": "test/device001/message",
  626. },
  627. },
  628. result: []map[string]interface{}{{}},
  629. },
  630. {
  631. sql: `SELECT split_value(a,"/",0) AS a, split_value(a,"/",3) AS b FROM test1`,
  632. data: &xsql.Tuple{
  633. Emitter: "test",
  634. Message: xsql.Message{
  635. "a": "/test/device001/message",
  636. },
  637. },
  638. result: []map[string]interface{}{{
  639. "a": "",
  640. "b": "message",
  641. }},
  642. },
  643. }
  644. fmt.Printf("The test bucket size is %d.\n\n", len(tests))
  645. contextLogger := conf.Log.WithField("rule", "TestStrFunc_Apply1")
  646. ctx := context.WithValue(context.Background(), context.LoggerKey, contextLogger)
  647. for i, tt := range tests {
  648. stmt, err := xsql.NewParser(strings.NewReader(tt.sql)).Parse()
  649. if err != nil || stmt == nil {
  650. t.Errorf("parse sql %s error %v", tt.sql, err)
  651. }
  652. pp := &ProjectOp{}
  653. parseStmt(pp, stmt.Fields)
  654. fv, afv := xsql.NewFunctionValuersForOp(nil)
  655. opResult := pp.Apply(ctx, tt.data, fv, afv)
  656. result, err := parseResult(opResult, pp.IsAggregate)
  657. if err != nil {
  658. t.Errorf("parse result error: %s", err)
  659. continue
  660. }
  661. if !reflect.DeepEqual(tt.result, result) {
  662. t.Errorf("%d. %q\n\nresult mismatch:\n\nexp=%#v\n\ngot=%#v\n\n", i, tt.sql, tt.result, result)
  663. }
  664. }
  665. }