udt_test.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  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. }