metadata.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883
  1. // Copyright (c) 2015 The gocql Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package gocql
  5. import (
  6. "encoding/hex"
  7. "encoding/json"
  8. "fmt"
  9. "log"
  10. "strconv"
  11. "strings"
  12. "sync"
  13. )
  14. // schema metadata for a keyspace
  15. type KeyspaceMetadata struct {
  16. Name string
  17. DurableWrites bool
  18. StrategyClass string
  19. StrategyOptions map[string]interface{}
  20. Tables map[string]*TableMetadata
  21. }
  22. // schema metadata for a table (a.k.a. column family)
  23. type TableMetadata struct {
  24. Keyspace string
  25. Name string
  26. KeyValidator string
  27. Comparator string
  28. DefaultValidator string
  29. KeyAliases []string
  30. ColumnAliases []string
  31. ValueAlias string
  32. PartitionKey []*ColumnMetadata
  33. ClusteringColumns []*ColumnMetadata
  34. Columns map[string]*ColumnMetadata
  35. OrderedColumns []string
  36. }
  37. // schema metadata for a column
  38. type ColumnMetadata struct {
  39. Keyspace string
  40. Table string
  41. Name string
  42. ComponentIndex int
  43. Kind string
  44. Validator string
  45. Type TypeInfo
  46. Order ColumnOrder
  47. Index ColumnIndexMetadata
  48. }
  49. // the ordering of the column with regard to its comparator
  50. type ColumnOrder bool
  51. const (
  52. ASC ColumnOrder = false
  53. DESC = true
  54. )
  55. type ColumnIndexMetadata struct {
  56. Name string
  57. Type string
  58. Options map[string]interface{}
  59. }
  60. // Column kind values
  61. const (
  62. PARTITION_KEY = "partition_key"
  63. CLUSTERING_KEY = "clustering_key"
  64. REGULAR = "regular"
  65. COMPACT_VALUE = "compact_value"
  66. )
  67. // default alias values
  68. const (
  69. DEFAULT_KEY_ALIAS = "key"
  70. DEFAULT_COLUMN_ALIAS = "column"
  71. DEFAULT_VALUE_ALIAS = "value"
  72. )
  73. // queries the cluster for schema information for a specific keyspace
  74. type schemaDescriber struct {
  75. session *Session
  76. mu sync.Mutex
  77. cache map[string]*KeyspaceMetadata
  78. }
  79. // creates a session bound schema describer which will query and cache
  80. // keyspace metadata
  81. func newSchemaDescriber(session *Session) *schemaDescriber {
  82. return &schemaDescriber{
  83. session: session,
  84. cache: map[string]*KeyspaceMetadata{},
  85. }
  86. }
  87. // returns the cached KeyspaceMetadata held by the describer for the named
  88. // keyspace.
  89. func (s *schemaDescriber) getSchema(keyspaceName string) (*KeyspaceMetadata, error) {
  90. s.mu.Lock()
  91. defer s.mu.Unlock()
  92. // TODO handle schema change events
  93. metadata, found := s.cache[keyspaceName]
  94. if !found {
  95. // refresh the cache for this keyspace
  96. err := s.refreshSchema(keyspaceName)
  97. if err != nil {
  98. return nil, err
  99. }
  100. metadata = s.cache[keyspaceName]
  101. }
  102. return metadata, nil
  103. }
  104. // forcibly updates the current KeyspaceMetadata held by the schema describer
  105. // for a given named keyspace.
  106. func (s *schemaDescriber) refreshSchema(keyspaceName string) error {
  107. var err error
  108. // query the system keyspace for schema data
  109. // TODO retrieve concurrently
  110. keyspace, err := getKeyspaceMetadata(s.session, keyspaceName)
  111. if err != nil {
  112. return err
  113. }
  114. tables, err := getTableMetadata(s.session, keyspaceName)
  115. if err != nil {
  116. return err
  117. }
  118. columns, err := getColumnMetadata(s.session, keyspaceName)
  119. if err != nil {
  120. return err
  121. }
  122. // organize the schema data
  123. compileMetadata(s.session.cfg.ProtoVersion, keyspace, tables, columns)
  124. // update the cache
  125. s.cache[keyspaceName] = keyspace
  126. return nil
  127. }
  128. // "compiles" derived information about keyspace, table, and column metadata
  129. // for a keyspace from the basic queried metadata objects returned by
  130. // getKeyspaceMetadata, getTableMetadata, and getColumnMetadata respectively;
  131. // Links the metadata objects together and derives the column composition of
  132. // the partition key and clustering key for a table.
  133. func compileMetadata(
  134. protoVersion int,
  135. keyspace *KeyspaceMetadata,
  136. tables []TableMetadata,
  137. columns []ColumnMetadata,
  138. ) {
  139. keyspace.Tables = make(map[string]*TableMetadata)
  140. for i := range tables {
  141. tables[i].Columns = make(map[string]*ColumnMetadata)
  142. keyspace.Tables[tables[i].Name] = &tables[i]
  143. }
  144. // add columns from the schema data
  145. for i := range columns {
  146. // decode the validator for TypeInfo and order
  147. validatorParsed := parseType(columns[i].Validator)
  148. columns[i].Type = validatorParsed.types[0]
  149. columns[i].Order = ASC
  150. if validatorParsed.reversed[0] {
  151. columns[i].Order = DESC
  152. }
  153. table := keyspace.Tables[columns[i].Table]
  154. table.Columns[columns[i].Name] = &columns[i]
  155. table.OrderedColumns = append(table.OrderedColumns, columns[i].Name)
  156. }
  157. if protoVersion == 1 {
  158. compileV1Metadata(tables)
  159. } else {
  160. compileV2Metadata(tables)
  161. }
  162. }
  163. // Compiles derived information from TableMetadata which have had
  164. // ColumnMetadata added already. V1 protocol does not return as much
  165. // column metadata as V2+ (because V1 doesn't support the "type" column in the
  166. // system.schema_columns table) so determining PartitionKey and ClusterColumns
  167. // is more complex.
  168. func compileV1Metadata(tables []TableMetadata) {
  169. for i := range tables {
  170. table := &tables[i]
  171. // decode the key validator
  172. keyValidatorParsed := parseType(table.KeyValidator)
  173. // decode the comparator
  174. comparatorParsed := parseType(table.Comparator)
  175. // the partition key length is the same as the number of types in the
  176. // key validator
  177. table.PartitionKey = make([]*ColumnMetadata, len(keyValidatorParsed.types))
  178. // V1 protocol only returns "regular" columns from
  179. // system.schema_columns (there is no type field for columns)
  180. // so the alias information is used to
  181. // create the partition key and clustering columns
  182. // construct the partition key from the alias
  183. for i := range table.PartitionKey {
  184. var alias string
  185. if len(table.KeyAliases) > i {
  186. alias = table.KeyAliases[i]
  187. } else if i == 0 {
  188. alias = DEFAULT_KEY_ALIAS
  189. } else {
  190. alias = DEFAULT_KEY_ALIAS + strconv.Itoa(i+1)
  191. }
  192. column := &ColumnMetadata{
  193. Keyspace: table.Keyspace,
  194. Table: table.Name,
  195. Name: alias,
  196. Type: keyValidatorParsed.types[i],
  197. Kind: PARTITION_KEY,
  198. ComponentIndex: i,
  199. }
  200. table.PartitionKey[i] = column
  201. table.Columns[alias] = column
  202. }
  203. // determine the number of clustering columns
  204. size := len(comparatorParsed.types)
  205. if comparatorParsed.isComposite {
  206. if len(comparatorParsed.collections) != 0 ||
  207. (len(table.ColumnAliases) == size-1 &&
  208. comparatorParsed.types[size-1].Type() == TypeVarchar) {
  209. size = size - 1
  210. }
  211. } else {
  212. if !(len(table.ColumnAliases) != 0 || len(table.Columns) == 0) {
  213. size = 0
  214. }
  215. }
  216. table.ClusteringColumns = make([]*ColumnMetadata, size)
  217. for i := range table.ClusteringColumns {
  218. var alias string
  219. if len(table.ColumnAliases) > i {
  220. alias = table.ColumnAliases[i]
  221. } else if i == 0 {
  222. alias = DEFAULT_COLUMN_ALIAS
  223. } else {
  224. alias = DEFAULT_COLUMN_ALIAS + strconv.Itoa(i+1)
  225. }
  226. order := ASC
  227. if comparatorParsed.reversed[i] {
  228. order = DESC
  229. }
  230. column := &ColumnMetadata{
  231. Keyspace: table.Keyspace,
  232. Table: table.Name,
  233. Name: alias,
  234. Type: comparatorParsed.types[i],
  235. Order: order,
  236. Kind: CLUSTERING_KEY,
  237. ComponentIndex: i,
  238. }
  239. table.ClusteringColumns[i] = column
  240. table.Columns[alias] = column
  241. }
  242. if size != len(comparatorParsed.types)-1 {
  243. alias := DEFAULT_VALUE_ALIAS
  244. if len(table.ValueAlias) > 0 {
  245. alias = table.ValueAlias
  246. }
  247. // decode the default validator
  248. defaultValidatorParsed := parseType(table.DefaultValidator)
  249. column := &ColumnMetadata{
  250. Keyspace: table.Keyspace,
  251. Table: table.Name,
  252. Name: alias,
  253. Type: defaultValidatorParsed.types[0],
  254. Kind: REGULAR,
  255. }
  256. table.Columns[alias] = column
  257. }
  258. }
  259. }
  260. // The simpler compile case for V2+ protocol
  261. func compileV2Metadata(tables []TableMetadata) {
  262. for i := range tables {
  263. table := &tables[i]
  264. keyValidatorParsed := parseType(table.KeyValidator)
  265. table.PartitionKey = make([]*ColumnMetadata, len(keyValidatorParsed.types))
  266. clusteringColumnCount := componentColumnCountOfType(table.Columns, CLUSTERING_KEY)
  267. table.ClusteringColumns = make([]*ColumnMetadata, clusteringColumnCount)
  268. for _, columnName := range table.OrderedColumns {
  269. column := table.Columns[columnName]
  270. if column.Kind == PARTITION_KEY {
  271. table.PartitionKey[column.ComponentIndex] = column
  272. } else if column.Kind == CLUSTERING_KEY {
  273. table.ClusteringColumns[column.ComponentIndex] = column
  274. }
  275. }
  276. }
  277. }
  278. // returns the count of coluns with the given "kind" value.
  279. func componentColumnCountOfType(columns map[string]*ColumnMetadata, kind string) int {
  280. maxComponentIndex := -1
  281. for _, column := range columns {
  282. if column.Kind == kind && column.ComponentIndex > maxComponentIndex {
  283. maxComponentIndex = column.ComponentIndex
  284. }
  285. }
  286. return maxComponentIndex + 1
  287. }
  288. // query only for the keyspace metadata for the specified keyspace from system.schema_keyspace
  289. func getKeyspaceMetadata(session *Session, keyspaceName string) (*KeyspaceMetadata, error) {
  290. const stmt = `
  291. SELECT durable_writes, strategy_class, strategy_options
  292. FROM system.schema_keyspaces
  293. WHERE keyspace_name = ?`
  294. keyspace := &KeyspaceMetadata{Name: keyspaceName}
  295. var strategyOptionsJSON []byte
  296. iter := session.control.query(stmt, keyspaceName)
  297. iter.Scan(&keyspace.DurableWrites, &keyspace.StrategyClass, &strategyOptionsJSON)
  298. err := iter.Close()
  299. if err != nil {
  300. return nil, fmt.Errorf("Error querying keyspace schema: %v", err)
  301. }
  302. err = json.Unmarshal(strategyOptionsJSON, &keyspace.StrategyOptions)
  303. if err != nil {
  304. return nil, fmt.Errorf(
  305. "Invalid JSON value '%s' as strategy_options for in keyspace '%s': %v",
  306. strategyOptionsJSON, keyspace.Name, err,
  307. )
  308. }
  309. return keyspace, nil
  310. }
  311. // query for only the table metadata in the specified keyspace from system.schema_columnfamilies
  312. func getTableMetadata(session *Session, keyspaceName string) ([]TableMetadata, error) {
  313. var (
  314. scan func(iter *Iter, table *TableMetadata) bool
  315. stmt string
  316. keyAliasesJSON []byte
  317. columnAliasesJSON []byte
  318. )
  319. if session.cfg.ProtoVersion < protoVersion4 {
  320. // we have key aliases
  321. // TODO: Do we need key_aliases?
  322. stmt = `
  323. SELECT
  324. columnfamily_name,
  325. key_validator,
  326. comparator,
  327. default_validator,
  328. key_aliases,
  329. column_aliases,
  330. value_alias
  331. FROM system.schema_columnfamilies
  332. WHERE keyspace_name = ?`
  333. scan = func(iter *Iter, table *TableMetadata) bool {
  334. return iter.Scan(
  335. &table.Name,
  336. &table.KeyValidator,
  337. &table.Comparator,
  338. &table.DefaultValidator,
  339. &keyAliasesJSON,
  340. &columnAliasesJSON,
  341. &table.ValueAlias,
  342. )
  343. }
  344. } else {
  345. stmt = `
  346. SELECT
  347. columnfamily_name,
  348. key_validator,
  349. comparator,
  350. default_validator
  351. FROM system.schema_columnfamilies
  352. WHERE keyspace_name = ?`
  353. scan = func(iter *Iter, table *TableMetadata) bool {
  354. return iter.Scan(
  355. &table.Name,
  356. &table.KeyValidator,
  357. &table.Comparator,
  358. &table.DefaultValidator,
  359. )
  360. }
  361. }
  362. iter := session.control.query(stmt, keyspaceName)
  363. tables := []TableMetadata{}
  364. table := TableMetadata{Keyspace: keyspaceName}
  365. for scan(iter, &table) {
  366. var err error
  367. // decode the key aliases
  368. if keyAliasesJSON != nil {
  369. table.KeyAliases = []string{}
  370. err = json.Unmarshal(keyAliasesJSON, &table.KeyAliases)
  371. if err != nil {
  372. iter.Close()
  373. return nil, fmt.Errorf(
  374. "Invalid JSON value '%s' as key_aliases for in table '%s': %v",
  375. keyAliasesJSON, table.Name, err,
  376. )
  377. }
  378. }
  379. // decode the column aliases
  380. if columnAliasesJSON != nil {
  381. table.ColumnAliases = []string{}
  382. err = json.Unmarshal(columnAliasesJSON, &table.ColumnAliases)
  383. if err != nil {
  384. iter.Close()
  385. return nil, fmt.Errorf(
  386. "Invalid JSON value '%s' as column_aliases for in table '%s': %v",
  387. columnAliasesJSON, table.Name, err,
  388. )
  389. }
  390. }
  391. tables = append(tables, table)
  392. table = TableMetadata{Keyspace: keyspaceName}
  393. }
  394. err := iter.Close()
  395. if err != nil && err != ErrNotFound {
  396. return nil, fmt.Errorf("Error querying table schema: %v", err)
  397. }
  398. return tables, nil
  399. }
  400. // query for only the column metadata in the specified keyspace from system.schema_columns
  401. func getColumnMetadata(
  402. session *Session,
  403. keyspaceName string,
  404. ) ([]ColumnMetadata, error) {
  405. // Deal with differences in protocol versions
  406. var stmt string
  407. var scan func(*Iter, *ColumnMetadata, *[]byte) bool
  408. if session.cfg.ProtoVersion == 1 {
  409. // V1 does not support the type column, and all returned rows are
  410. // of kind "regular".
  411. stmt = `
  412. SELECT
  413. columnfamily_name,
  414. column_name,
  415. component_index,
  416. validator,
  417. index_name,
  418. index_type,
  419. index_options
  420. FROM system.schema_columns
  421. WHERE keyspace_name = ?
  422. `
  423. scan = func(
  424. iter *Iter,
  425. column *ColumnMetadata,
  426. indexOptionsJSON *[]byte,
  427. ) bool {
  428. // all columns returned by V1 are regular
  429. column.Kind = REGULAR
  430. return iter.Scan(
  431. &column.Table,
  432. &column.Name,
  433. &column.ComponentIndex,
  434. &column.Validator,
  435. &column.Index.Name,
  436. &column.Index.Type,
  437. &indexOptionsJSON,
  438. )
  439. }
  440. } else {
  441. // V2+ supports the type column
  442. stmt = `
  443. SELECT
  444. columnfamily_name,
  445. column_name,
  446. component_index,
  447. validator,
  448. index_name,
  449. index_type,
  450. index_options,
  451. type
  452. FROM system.schema_columns
  453. WHERE keyspace_name = ?
  454. `
  455. scan = func(
  456. iter *Iter,
  457. column *ColumnMetadata,
  458. indexOptionsJSON *[]byte,
  459. ) bool {
  460. return iter.Scan(
  461. &column.Table,
  462. &column.Name,
  463. &column.ComponentIndex,
  464. &column.Validator,
  465. &column.Index.Name,
  466. &column.Index.Type,
  467. &indexOptionsJSON,
  468. &column.Kind,
  469. )
  470. }
  471. }
  472. // get the columns metadata
  473. columns := []ColumnMetadata{}
  474. column := ColumnMetadata{Keyspace: keyspaceName}
  475. var indexOptionsJSON []byte
  476. iter := session.control.query(stmt, keyspaceName)
  477. for scan(iter, &column, &indexOptionsJSON) {
  478. var err error
  479. // decode the index options
  480. if indexOptionsJSON != nil {
  481. err = json.Unmarshal(indexOptionsJSON, &column.Index.Options)
  482. if err != nil {
  483. iter.Close()
  484. return nil, fmt.Errorf(
  485. "Invalid JSON value '%s' as index_options for column '%s' in table '%s': %v",
  486. indexOptionsJSON,
  487. column.Name,
  488. column.Table,
  489. err,
  490. )
  491. }
  492. }
  493. columns = append(columns, column)
  494. column = ColumnMetadata{Keyspace: keyspaceName}
  495. }
  496. err := iter.Close()
  497. if err != nil && err != ErrNotFound {
  498. return nil, fmt.Errorf("Error querying column schema: %v", err)
  499. }
  500. return columns, nil
  501. }
  502. // type definition parser state
  503. type typeParser struct {
  504. input string
  505. index int
  506. }
  507. // the type definition parser result
  508. type typeParserResult struct {
  509. isComposite bool
  510. types []TypeInfo
  511. reversed []bool
  512. collections map[string]TypeInfo
  513. }
  514. // Parse the type definition used for validator and comparator schema data
  515. func parseType(def string) typeParserResult {
  516. parser := &typeParser{input: def}
  517. return parser.parse()
  518. }
  519. const (
  520. REVERSED_TYPE = "org.apache.cassandra.db.marshal.ReversedType"
  521. COMPOSITE_TYPE = "org.apache.cassandra.db.marshal.CompositeType"
  522. COLLECTION_TYPE = "org.apache.cassandra.db.marshal.ColumnToCollectionType"
  523. LIST_TYPE = "org.apache.cassandra.db.marshal.ListType"
  524. SET_TYPE = "org.apache.cassandra.db.marshal.SetType"
  525. MAP_TYPE = "org.apache.cassandra.db.marshal.MapType"
  526. )
  527. // represents a class specification in the type def AST
  528. type typeParserClassNode struct {
  529. name string
  530. params []typeParserParamNode
  531. // this is the segment of the input string that defined this node
  532. input string
  533. }
  534. // represents a class parameter in the type def AST
  535. type typeParserParamNode struct {
  536. name *string
  537. class typeParserClassNode
  538. }
  539. func (t *typeParser) parse() typeParserResult {
  540. // parse the AST
  541. ast, ok := t.parseClassNode()
  542. if !ok {
  543. // treat this is a custom type
  544. return typeParserResult{
  545. isComposite: false,
  546. types: []TypeInfo{
  547. NativeType{
  548. typ: TypeCustom,
  549. custom: t.input,
  550. },
  551. },
  552. reversed: []bool{false},
  553. collections: nil,
  554. }
  555. }
  556. // interpret the AST
  557. if strings.HasPrefix(ast.name, COMPOSITE_TYPE) {
  558. count := len(ast.params)
  559. // look for a collections param
  560. last := ast.params[count-1]
  561. collections := map[string]TypeInfo{}
  562. if strings.HasPrefix(last.class.name, COLLECTION_TYPE) {
  563. count--
  564. for _, param := range last.class.params {
  565. // decode the name
  566. var name string
  567. decoded, err := hex.DecodeString(*param.name)
  568. if err != nil {
  569. log.Printf(
  570. "Error parsing type '%s', contains collection name '%s' with an invalid format: %v",
  571. t.input,
  572. *param.name,
  573. err,
  574. )
  575. // just use the provided name
  576. name = *param.name
  577. } else {
  578. name = string(decoded)
  579. }
  580. collections[name] = param.class.asTypeInfo()
  581. }
  582. }
  583. types := make([]TypeInfo, count)
  584. reversed := make([]bool, count)
  585. for i, param := range ast.params[:count] {
  586. class := param.class
  587. reversed[i] = strings.HasPrefix(class.name, REVERSED_TYPE)
  588. if reversed[i] {
  589. class = class.params[0].class
  590. }
  591. types[i] = class.asTypeInfo()
  592. }
  593. return typeParserResult{
  594. isComposite: true,
  595. types: types,
  596. reversed: reversed,
  597. collections: collections,
  598. }
  599. } else {
  600. // not composite, so one type
  601. class := *ast
  602. reversed := strings.HasPrefix(class.name, REVERSED_TYPE)
  603. if reversed {
  604. class = class.params[0].class
  605. }
  606. typeInfo := class.asTypeInfo()
  607. return typeParserResult{
  608. isComposite: false,
  609. types: []TypeInfo{typeInfo},
  610. reversed: []bool{reversed},
  611. }
  612. }
  613. }
  614. func (class *typeParserClassNode) asTypeInfo() TypeInfo {
  615. if strings.HasPrefix(class.name, LIST_TYPE) {
  616. elem := class.params[0].class.asTypeInfo()
  617. return CollectionType{
  618. NativeType: NativeType{
  619. typ: TypeList,
  620. },
  621. Elem: elem,
  622. }
  623. }
  624. if strings.HasPrefix(class.name, SET_TYPE) {
  625. elem := class.params[0].class.asTypeInfo()
  626. return CollectionType{
  627. NativeType: NativeType{
  628. typ: TypeSet,
  629. },
  630. Elem: elem,
  631. }
  632. }
  633. if strings.HasPrefix(class.name, MAP_TYPE) {
  634. key := class.params[0].class.asTypeInfo()
  635. elem := class.params[1].class.asTypeInfo()
  636. return CollectionType{
  637. NativeType: NativeType{
  638. typ: TypeMap,
  639. },
  640. Key: key,
  641. Elem: elem,
  642. }
  643. }
  644. // must be a simple type or custom type
  645. info := NativeType{typ: getApacheCassandraType(class.name)}
  646. if info.typ == TypeCustom {
  647. // add the entire class definition
  648. info.custom = class.input
  649. }
  650. return info
  651. }
  652. // CLASS := ID [ PARAMS ]
  653. func (t *typeParser) parseClassNode() (node *typeParserClassNode, ok bool) {
  654. t.skipWhitespace()
  655. startIndex := t.index
  656. name, ok := t.nextIdentifier()
  657. if !ok {
  658. return nil, false
  659. }
  660. params, ok := t.parseParamNodes()
  661. if !ok {
  662. return nil, false
  663. }
  664. endIndex := t.index
  665. node = &typeParserClassNode{
  666. name: name,
  667. params: params,
  668. input: t.input[startIndex:endIndex],
  669. }
  670. return node, true
  671. }
  672. // PARAMS := "(" PARAM { "," PARAM } ")"
  673. // PARAM := [ PARAM_NAME ":" ] CLASS
  674. // PARAM_NAME := ID
  675. func (t *typeParser) parseParamNodes() (params []typeParserParamNode, ok bool) {
  676. t.skipWhitespace()
  677. // the params are optional
  678. if t.index == len(t.input) || t.input[t.index] != '(' {
  679. return nil, true
  680. }
  681. params = []typeParserParamNode{}
  682. // consume the '('
  683. t.index++
  684. t.skipWhitespace()
  685. for t.input[t.index] != ')' {
  686. // look for a named param, but if no colon, then we want to backup
  687. backupIndex := t.index
  688. // name will be a hex encoded version of a utf-8 string
  689. name, ok := t.nextIdentifier()
  690. if !ok {
  691. return nil, false
  692. }
  693. hasName := true
  694. // TODO handle '=>' used for DynamicCompositeType
  695. t.skipWhitespace()
  696. if t.input[t.index] == ':' {
  697. // there is a name for this parameter
  698. // consume the ':'
  699. t.index++
  700. t.skipWhitespace()
  701. } else {
  702. // no name, backup
  703. hasName = false
  704. t.index = backupIndex
  705. }
  706. // parse the next full parameter
  707. classNode, ok := t.parseClassNode()
  708. if !ok {
  709. return nil, false
  710. }
  711. if hasName {
  712. params = append(
  713. params,
  714. typeParserParamNode{name: &name, class: *classNode},
  715. )
  716. } else {
  717. params = append(
  718. params,
  719. typeParserParamNode{class: *classNode},
  720. )
  721. }
  722. t.skipWhitespace()
  723. if t.input[t.index] == ',' {
  724. // consume the comma
  725. t.index++
  726. t.skipWhitespace()
  727. }
  728. }
  729. // consume the ')'
  730. t.index++
  731. return params, true
  732. }
  733. func (t *typeParser) skipWhitespace() {
  734. for t.index < len(t.input) && isWhitespaceChar(t.input[t.index]) {
  735. t.index++
  736. }
  737. }
  738. func isWhitespaceChar(c byte) bool {
  739. return c == ' ' || c == '\n' || c == '\t'
  740. }
  741. // ID := LETTER { LETTER }
  742. // LETTER := "0"..."9" | "a"..."z" | "A"..."Z" | "-" | "+" | "." | "_" | "&"
  743. func (t *typeParser) nextIdentifier() (id string, found bool) {
  744. startIndex := t.index
  745. for t.index < len(t.input) && isIdentifierChar(t.input[t.index]) {
  746. t.index++
  747. }
  748. if startIndex == t.index {
  749. return "", false
  750. }
  751. return t.input[startIndex:t.index], true
  752. }
  753. func isIdentifierChar(c byte) bool {
  754. return (c >= '0' && c <= '9') ||
  755. (c >= 'a' && c <= 'z') ||
  756. (c >= 'A' && c <= 'Z') ||
  757. c == '-' ||
  758. c == '+' ||
  759. c == '.' ||
  760. c == '_' ||
  761. c == '&'
  762. }