udt_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. // +build all integration
  2. package gocql
  3. import (
  4. "fmt"
  5. "strings"
  6. "testing"
  7. "time"
  8. )
  9. type position struct {
  10. Lat int `cql:"lat"`
  11. Lon int `cql:"lon"`
  12. Padding string `json:"padding"`
  13. }
  14. // NOTE: due to current implementation details it is not currently possible to use
  15. // a pointer receiver type for the UDTMarshaler interface to handle UDT's
  16. func (p position) MarshalUDT(name string, info TypeInfo) ([]byte, error) {
  17. switch name {
  18. case "lat":
  19. return Marshal(info, p.Lat)
  20. case "lon":
  21. return Marshal(info, p.Lon)
  22. case "padding":
  23. return Marshal(info, p.Padding)
  24. default:
  25. return nil, fmt.Errorf("unknown column for position: %q", name)
  26. }
  27. }
  28. func (p *position) UnmarshalUDT(name string, info TypeInfo, data []byte) error {
  29. switch name {
  30. case "lat":
  31. return Unmarshal(info, data, &p.Lat)
  32. case "lon":
  33. return Unmarshal(info, data, &p.Lon)
  34. case "padding":
  35. return Unmarshal(info, data, &p.Padding)
  36. default:
  37. return fmt.Errorf("unknown column for position: %q", name)
  38. }
  39. }
  40. func TestUDT_Marshaler(t *testing.T) {
  41. if *flagProto < protoVersion3 {
  42. t.Skip("UDT are only available on protocol >= 3")
  43. }
  44. session := createSession(t)
  45. defer session.Close()
  46. err := createTable(session, `CREATE TYPE gocql_test.position(
  47. lat int,
  48. lon int,
  49. padding text);`)
  50. if err != nil {
  51. t.Fatal(err)
  52. }
  53. err = createTable(session, `CREATE TABLE gocql_test.houses(
  54. id int,
  55. name text,
  56. loc frozen<position>,
  57. primary key(id)
  58. );`)
  59. if err != nil {
  60. t.Fatal(err)
  61. }
  62. const (
  63. expLat = -1
  64. expLon = 2
  65. )
  66. pad := strings.Repeat("X", 1000)
  67. err = session.Query("INSERT INTO houses(id, name, loc) VALUES(?, ?, ?)", 1, "test", &position{expLat, expLon, pad}).Exec()
  68. if err != nil {
  69. t.Fatal(err)
  70. }
  71. pos := &position{}
  72. err = session.Query("SELECT loc FROM houses WHERE id = ?", 1).Scan(pos)
  73. if err != nil {
  74. t.Fatal(err)
  75. }
  76. if pos.Lat != expLat {
  77. t.Errorf("expeceted lat to be be %d got %d", expLat, pos.Lat)
  78. }
  79. if pos.Lon != expLon {
  80. t.Errorf("expeceted lon to be be %d got %d", expLon, pos.Lon)
  81. }
  82. if pos.Padding != pad {
  83. t.Errorf("expected to get padding %q got %q\n", pad, pos.Padding)
  84. }
  85. }
  86. func TestUDT_Reflect(t *testing.T) {
  87. if *flagProto < protoVersion3 {
  88. t.Skip("UDT are only available on protocol >= 3")
  89. }
  90. // Uses reflection instead of implementing the marshaling type
  91. session := createSession(t)
  92. defer session.Close()
  93. err := createTable(session, `CREATE TYPE gocql_test.horse(
  94. name text,
  95. owner text);`)
  96. if err != nil {
  97. t.Fatal(err)
  98. }
  99. err = createTable(session, `CREATE TABLE gocql_test.horse_race(
  100. position int,
  101. horse frozen<horse>,
  102. primary key(position)
  103. );`)
  104. if err != nil {
  105. t.Fatal(err)
  106. }
  107. type horse struct {
  108. Name string `cql:"name"`
  109. Owner string `cql:"owner"`
  110. }
  111. insertedHorse := &horse{
  112. Name: "pony",
  113. Owner: "jim",
  114. }
  115. err = session.Query("INSERT INTO horse_race(position, horse) VALUES(?, ?)", 1, insertedHorse).Exec()
  116. if err != nil {
  117. t.Fatal(err)
  118. }
  119. retrievedHorse := &horse{}
  120. err = session.Query("SELECT horse FROM horse_race WHERE position = ?", 1).Scan(retrievedHorse)
  121. if err != nil {
  122. t.Fatal(err)
  123. }
  124. if *retrievedHorse != *insertedHorse {
  125. t.Fatal("exepcted to get %+v got %+v", insertedHorse, retrievedHorse)
  126. }
  127. }
  128. func TestUDT_Proto2error(t *testing.T) {
  129. if *flagProto < protoVersion3 {
  130. t.Skip("UDT are only available on protocol >= 3")
  131. }
  132. cluster := createCluster()
  133. cluster.ProtoVersion = 2
  134. cluster.Keyspace = "gocql_test"
  135. // Uses reflection instead of implementing the marshaling type
  136. session, err := cluster.CreateSession()
  137. if err != nil {
  138. t.Fatal(err)
  139. }
  140. defer session.Close()
  141. err = createTable(session, `CREATE TYPE gocql_test.fish(
  142. name text,
  143. owner text);`)
  144. if err != nil {
  145. t.Fatal(err)
  146. }
  147. err = createTable(session, `CREATE TABLE gocql_test.fish_race(
  148. position int,
  149. fish frozen<fish>,
  150. primary key(position)
  151. );`)
  152. if err != nil {
  153. t.Fatal(err)
  154. }
  155. type fish struct {
  156. Name string `cql:"name"`
  157. Owner string `cql:"owner"`
  158. }
  159. insertedFish := &fish{
  160. Name: "pony",
  161. Owner: "jim",
  162. }
  163. err = session.Query("INSERT INTO fish_race(position, fish) VALUES(?, ?)", 1, insertedFish).Exec()
  164. if err != ErrorUDTUnavailable {
  165. t.Fatalf("expected to get %v got %v", ErrorUDTUnavailable, err)
  166. }
  167. }
  168. func TestUDT_NullObject(t *testing.T) {
  169. if *flagProto < protoVersion3 {
  170. t.Skip("UDT are only available on protocol >= 3")
  171. }
  172. session := createSession(t)
  173. defer session.Close()
  174. err := createTable(session, `CREATE TYPE gocql_test.udt_null_type(
  175. name text,
  176. owner text);`)
  177. if err != nil {
  178. t.Fatal(err)
  179. }
  180. err = createTable(session, `CREATE TABLE gocql_test.udt_null_table(
  181. id uuid,
  182. udt_col frozen<udt_null_type>,
  183. primary key(id)
  184. );`)
  185. if err != nil {
  186. t.Fatal(err)
  187. }
  188. type col struct {
  189. Name string `cql:"name"`
  190. Owner string `cql:"owner"`
  191. }
  192. id := TimeUUID()
  193. err = session.Query("INSERT INTO udt_null_table(id) VALUES(?)", id).Exec()
  194. if err != nil {
  195. t.Fatal(err)
  196. }
  197. readCol := &col{
  198. Name: "temp",
  199. Owner: "temp",
  200. }
  201. err = session.Query("SELECT udt_col FROM udt_null_table WHERE id = ?", id).Scan(readCol)
  202. if err != nil {
  203. t.Fatal(err)
  204. }
  205. if readCol.Name != "" {
  206. t.Errorf("expected empty string to be returned for null udt: got %q", readCol.Name)
  207. }
  208. if readCol.Owner != "" {
  209. t.Errorf("expected empty string to be returned for null udt: got %q", readCol.Owner)
  210. }
  211. }
  212. func TestMapScanUDT(t *testing.T) {
  213. if *flagProto < protoVersion3 {
  214. t.Skip("UDT are only available on protocol >= 3")
  215. }
  216. session := createSession(t)
  217. defer session.Close()
  218. err := createTable(session, `CREATE TYPE gocql_test.log_entry (
  219. created_timestamp timestamp,
  220. message text
  221. );`)
  222. if err != nil {
  223. t.Fatal(err)
  224. }
  225. err = createTable(session, `CREATE TABLE gocql_test.requests_by_id (
  226. id uuid PRIMARY KEY,
  227. type int,
  228. log_entries list<frozen <log_entry>>
  229. );`)
  230. if err != nil {
  231. t.Fatal(err)
  232. }
  233. entry := []struct {
  234. CreatedTimestamp time.Time `cql:"created_timestamp"`
  235. Message string `cql:"message"`
  236. }{
  237. {
  238. CreatedTimestamp: time.Now().Truncate(time.Millisecond),
  239. Message: "test time now",
  240. },
  241. }
  242. id, _ := RandomUUID()
  243. const typ = 1
  244. err = session.Query("INSERT INTO requests_by_id(id, type, log_entries) VALUES (?, ?, ?)", id, typ, entry).Exec()
  245. if err != nil {
  246. t.Fatal(err)
  247. }
  248. rawResult := map[string]interface{}{}
  249. err = session.Query(`SELECT * FROM requests_by_id WHERE id = ?`, id).MapScan(rawResult)
  250. if err != nil {
  251. t.Fatal(err)
  252. }
  253. logEntries, ok := rawResult["log_entries"].([]map[string]interface{})
  254. if !ok {
  255. t.Fatal("log_entries not in scanned map")
  256. }
  257. if len(logEntries) != 1 {
  258. t.Fatalf("expected to get 1 log_entry got %d", len(logEntries))
  259. }
  260. logEntry := logEntries[0]
  261. timestamp, ok := logEntry["created_timestamp"]
  262. if !ok {
  263. t.Error("created_timestamp not unmarshalled into map")
  264. } else {
  265. if ts, ok := timestamp.(time.Time); ok {
  266. if !ts.In(time.UTC).Equal(entry[0].CreatedTimestamp.In(time.UTC)) {
  267. t.Errorf("created_timestamp not equal to stored: got %v expected %v", ts.In(time.UTC), entry[0].CreatedTimestamp.In(time.UTC))
  268. }
  269. } else {
  270. t.Errorf("created_timestamp was not time.Time got: %T", timestamp)
  271. }
  272. }
  273. message, ok := logEntry["message"]
  274. if !ok {
  275. t.Error("message not unmarshalled into map")
  276. } else {
  277. if ts, ok := message.(string); ok {
  278. if ts != message {
  279. t.Errorf("message not equal to stored: got %v expected %v", ts, entry[0].Message)
  280. }
  281. } else {
  282. t.Errorf("message was not string got: %T", message)
  283. }
  284. }
  285. }
  286. func TestUDT_MissingField(t *testing.T) {
  287. if *flagProto < protoVersion3 {
  288. t.Skip("UDT are only available on protocol >= 3")
  289. }
  290. session := createSession(t)
  291. defer session.Close()
  292. err := createTable(session, `CREATE TYPE gocql_test.missing_field(
  293. name text,
  294. owner text);`)
  295. if err != nil {
  296. t.Fatal(err)
  297. }
  298. err = createTable(session, `CREATE TABLE gocql_test.missing_field(
  299. id uuid,
  300. udt_col frozen<udt_null_type>,
  301. primary key(id)
  302. );`)
  303. if err != nil {
  304. t.Fatal(err)
  305. }
  306. type col struct {
  307. Name string `cql:"name"`
  308. }
  309. writeCol := &col{
  310. Name: "test",
  311. }
  312. id := TimeUUID()
  313. err = session.Query("INSERT INTO missing_field(id, udt_col) VALUES(?, ?)", id, writeCol).Exec()
  314. if err != nil {
  315. t.Fatal(err)
  316. }
  317. readCol := &col{}
  318. err = session.Query("SELECT udt_col FROM missing_field WHERE id = ?", id).Scan(readCol)
  319. if err != nil {
  320. t.Fatal(err)
  321. }
  322. if readCol.Name != writeCol.Name {
  323. t.Errorf("expected %q: got %q", writeCol.Name, readCol.Name)
  324. }
  325. }
  326. func TestUDT_EmptyCollections(t *testing.T) {
  327. if *flagProto < protoVersion3 {
  328. t.Skip("UDT are only available on protocol >= 3")
  329. }
  330. session := createSession(t)
  331. defer session.Close()
  332. err := createTable(session, `CREATE TYPE gocql_test.nil_collections(
  333. a list<text>,
  334. b map<text, text>,
  335. c set<text>
  336. );`)
  337. if err != nil {
  338. t.Fatal(err)
  339. }
  340. err = createTable(session, `CREATE TABLE gocql_test.nil_collections(
  341. id uuid,
  342. udt_col frozen<nil_collections>,
  343. primary key(id)
  344. );`)
  345. if err != nil {
  346. t.Fatal(err)
  347. }
  348. id := TimeUUID()
  349. err = session.Query("INSERT INTO nil_collections(id, udt_col) VALUES(?, ?)", id, &struct{}{}).Exec()
  350. if err != nil {
  351. t.Fatal(err)
  352. }
  353. }
  354. func TestUDT_UpdateField(t *testing.T) {
  355. if *flagProto < protoVersion3 {
  356. t.Skip("UDT are only available on protocol >= 3")
  357. }
  358. session := createSession(t)
  359. defer session.Close()
  360. err := createTable(session, `CREATE TYPE gocql_test.update_field_udt(
  361. name text,
  362. owner text);`)
  363. if err != nil {
  364. t.Fatal(err)
  365. }
  366. err = createTable(session, `CREATE TABLE gocql_test.update_field(
  367. id uuid,
  368. udt_col frozen<update_field_udt>,
  369. primary key(id)
  370. );`)
  371. if err != nil {
  372. t.Fatal(err)
  373. }
  374. type col struct {
  375. Name string `cql:"name"`
  376. Owner string `cql:"owner"`
  377. Data string `cql:"data"`
  378. }
  379. writeCol := &col{
  380. Name: "test-name",
  381. Owner: "test-owner",
  382. }
  383. id := TimeUUID()
  384. err = session.Query("INSERT INTO update_field(id, udt_col) VALUES(?, ?)", id, writeCol).Exec()
  385. if err != nil {
  386. t.Fatal(err)
  387. }
  388. if err := createTable(session, `ALTER TYPE gocql_test.update_field_udt ADD data text;`); err != nil {
  389. t.Fatal(err)
  390. }
  391. readCol := &col{}
  392. err = session.Query("SELECT udt_col FROM update_field WHERE id = ?", id).Scan(readCol)
  393. if err != nil {
  394. t.Fatal(err)
  395. }
  396. if *readCol != *writeCol {
  397. t.Errorf("expected %+v: got %+v", *writeCol, *readCol)
  398. }
  399. }