expression.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. package expression
  2. import (
  3. "fmt"
  4. "sort"
  5. "github.com/aws/aws-sdk-go/aws"
  6. "github.com/aws/aws-sdk-go/service/dynamodb"
  7. )
  8. // expressionType specifies the type of Expression. Declaring this type is used
  9. // to eliminate magic strings
  10. type expressionType string
  11. const (
  12. projection expressionType = "projection"
  13. keyCondition = "keyCondition"
  14. condition = "condition"
  15. filter = "filter"
  16. update = "update"
  17. )
  18. // Implement the Sort interface
  19. type typeList []expressionType
  20. func (l typeList) Len() int {
  21. return len(l)
  22. }
  23. func (l typeList) Less(i, j int) bool {
  24. return string(l[i]) < string(l[j])
  25. }
  26. func (l typeList) Swap(i, j int) {
  27. l[i], l[j] = l[j], l[i]
  28. }
  29. // Builder represents the struct that builds the Expression struct. Methods such
  30. // as WithProjection() and WithCondition() can add different kinds of DynamoDB
  31. // Expressions to the Builder. The method Build() creates an Expression struct
  32. // with the specified types of DynamoDB Expressions.
  33. //
  34. // Example:
  35. //
  36. // keyCond := expression.Key("someKey").Equal(expression.Value("someValue"))
  37. // proj := expression.NamesList(expression.Name("aName"), expression.Name("anotherName"), expression.Name("oneOtherName"))
  38. //
  39. // builder := expression.NewBuilder().WithKeyCondition(keyCond).WithProjection(proj)
  40. // expr := builder.Build()
  41. //
  42. // queryInput := dynamodb.QueryInput{
  43. // KeyConditionExpression: expr.KeyCondition(),
  44. // ProjectionExpression: expr.Projection(),
  45. // ExpressionAttributeNames: expr.Names(),
  46. // ExpressionAttributeValues: expr.Values(),
  47. // TableName: aws.String("SomeTable"),
  48. // }
  49. type Builder struct {
  50. expressionMap map[expressionType]treeBuilder
  51. }
  52. // NewBuilder returns an empty Builder struct. Methods such as WithProjection()
  53. // and WithCondition() can add different kinds of DynamoDB Expressions to the
  54. // Builder. The method Build() creates an Expression struct with the specified
  55. // types of DynamoDB Expressions.
  56. //
  57. // Example:
  58. //
  59. // keyCond := expression.Key("someKey").Equal(expression.Value("someValue"))
  60. // proj := expression.NamesList(expression.Name("aName"), expression.Name("anotherName"), expression.Name("oneOtherName"))
  61. // builder := expression.NewBuilder().WithKeyCondition(keyCond).WithProjection(proj)
  62. func NewBuilder() Builder {
  63. return Builder{}
  64. }
  65. // Build builds an Expression struct representing multiple types of DynamoDB
  66. // Expressions. Getter methods on the resulting Expression struct returns the
  67. // DynamoDB Expression strings as well as the maps that correspond to
  68. // ExpressionAttributeNames and ExpressionAttributeValues. Calling Build() on an
  69. // empty Builder returns the typed error EmptyParameterError.
  70. //
  71. // Example:
  72. //
  73. // // keyCond represents the Key Condition Expression
  74. // keyCond := expression.Key("someKey").Equal(expression.Value("someValue"))
  75. // // proj represents the Projection Expression
  76. // proj := expression.NamesList(expression.Name("aName"), expression.Name("anotherName"), expression.Name("oneOtherName"))
  77. //
  78. // // Add keyCond and proj to builder as a Key Condition and Projection
  79. // // respectively
  80. // builder := expression.NewBuilder().WithKeyCondition(keyCond).WithProjection(proj)
  81. // expr := builder.Build()
  82. //
  83. // queryInput := dynamodb.QueryInput{
  84. // KeyConditionExpression: expr.KeyCondition(),
  85. // ProjectionExpression: expr.Projection(),
  86. // ExpressionAttributeNames: expr.Names(),
  87. // ExpressionAttributeValues: expr.Values(),
  88. // TableName: aws.String("SomeTable"),
  89. // }
  90. func (b Builder) Build() (Expression, error) {
  91. if b.expressionMap == nil {
  92. return Expression{}, newUnsetParameterError("Build", "Builder")
  93. }
  94. aliasList, expressionMap, err := b.buildChildTrees()
  95. if err != nil {
  96. return Expression{}, err
  97. }
  98. expression := Expression{
  99. expressionMap: expressionMap,
  100. }
  101. if len(aliasList.namesList) != 0 {
  102. namesMap := map[string]*string{}
  103. for ind, val := range aliasList.namesList {
  104. namesMap[fmt.Sprintf("#%v", ind)] = aws.String(val)
  105. }
  106. expression.namesMap = namesMap
  107. }
  108. if len(aliasList.valuesList) != 0 {
  109. valuesMap := map[string]*dynamodb.AttributeValue{}
  110. for i := 0; i < len(aliasList.valuesList); i++ {
  111. valuesMap[fmt.Sprintf(":%v", i)] = &aliasList.valuesList[i]
  112. }
  113. expression.valuesMap = valuesMap
  114. }
  115. return expression, nil
  116. }
  117. // buildChildTrees compiles the list of treeBuilders that are the children of
  118. // the argument Builder. The returned aliasList represents all the alias tokens
  119. // used in the expression strings. The returned map[string]string maps the type
  120. // of expression (i.e. "condition", "update") to the appropriate expression
  121. // string.
  122. func (b Builder) buildChildTrees() (aliasList, map[expressionType]string, error) {
  123. aList := aliasList{}
  124. formattedExpressions := map[expressionType]string{}
  125. keys := typeList{}
  126. for expressionType := range b.expressionMap {
  127. keys = append(keys, expressionType)
  128. }
  129. sort.Sort(keys)
  130. for _, key := range keys {
  131. node, err := b.expressionMap[key].buildTree()
  132. if err != nil {
  133. return aliasList{}, nil, err
  134. }
  135. formattedExpression, err := node.buildExpressionString(&aList)
  136. if err != nil {
  137. return aliasList{}, nil, err
  138. }
  139. formattedExpressions[key] = formattedExpression
  140. }
  141. return aList, formattedExpressions, nil
  142. }
  143. // WithCondition method adds the argument ConditionBuilder as a Condition
  144. // Expression to the argument Builder. If the argument Builder already has a
  145. // ConditionBuilder representing a Condition Expression, WithCondition()
  146. // overwrites the existing ConditionBuilder.
  147. //
  148. // Example:
  149. //
  150. // // let builder be an existing Builder{} and cond be an existing
  151. // // ConditionBuilder{}
  152. // builder = builder.WithCondition(cond)
  153. //
  154. // // add other DynamoDB Expressions to the builder. let proj be an already
  155. // // existing ProjectionBuilder
  156. // builder = builder.WithProjection(proj)
  157. // // create an Expression struct
  158. // expr := builder.Build()
  159. func (b Builder) WithCondition(conditionBuilder ConditionBuilder) Builder {
  160. if b.expressionMap == nil {
  161. b.expressionMap = map[expressionType]treeBuilder{}
  162. }
  163. b.expressionMap[condition] = conditionBuilder
  164. return b
  165. }
  166. // WithProjection method adds the argument ProjectionBuilder as a Projection
  167. // Expression to the argument Builder. If the argument Builder already has a
  168. // ProjectionBuilder representing a Projection Expression, WithProjection()
  169. // overwrites the existing ProjectionBuilder.
  170. //
  171. // Example:
  172. //
  173. // // let builder be an existing Builder{} and proj be an existing
  174. // // ProjectionBuilder{}
  175. // builder = builder.WithProjection(proj)
  176. //
  177. // // add other DynamoDB Expressions to the builder. let cond be an already
  178. // // existing ConditionBuilder
  179. // builder = builder.WithCondition(cond)
  180. // // create an Expression struct
  181. // expr := builder.Build()
  182. func (b Builder) WithProjection(projectionBuilder ProjectionBuilder) Builder {
  183. if b.expressionMap == nil {
  184. b.expressionMap = map[expressionType]treeBuilder{}
  185. }
  186. b.expressionMap[projection] = projectionBuilder
  187. return b
  188. }
  189. // WithKeyCondition method adds the argument KeyConditionBuilder as a Key
  190. // Condition Expression to the argument Builder. If the argument Builder already
  191. // has a KeyConditionBuilder representing a Key Condition Expression,
  192. // WithKeyCondition() overwrites the existing KeyConditionBuilder.
  193. //
  194. // Example:
  195. //
  196. // // let builder be an existing Builder{} and keyCond be an existing
  197. // // KeyConditionBuilder{}
  198. // builder = builder.WithKeyCondition(keyCond)
  199. //
  200. // // add other DynamoDB Expressions to the builder. let cond be an already
  201. // // existing ConditionBuilder
  202. // builder = builder.WithCondition(cond)
  203. // // create an Expression struct
  204. // expr := builder.Build()
  205. func (b Builder) WithKeyCondition(keyConditionBuilder KeyConditionBuilder) Builder {
  206. if b.expressionMap == nil {
  207. b.expressionMap = map[expressionType]treeBuilder{}
  208. }
  209. b.expressionMap[keyCondition] = keyConditionBuilder
  210. return b
  211. }
  212. // WithFilter method adds the argument ConditionBuilder as a Filter Expression
  213. // to the argument Builder. If the argument Builder already has a
  214. // ConditionBuilder representing a Filter Expression, WithFilter()
  215. // overwrites the existing ConditionBuilder.
  216. //
  217. // Example:
  218. //
  219. // // let builder be an existing Builder{} and filt be an existing
  220. // // ConditionBuilder{}
  221. // builder = builder.WithFilter(filt)
  222. //
  223. // // add other DynamoDB Expressions to the builder. let cond be an already
  224. // // existing ConditionBuilder
  225. // builder = builder.WithCondition(cond)
  226. // // create an Expression struct
  227. // expr := builder.Build()
  228. func (b Builder) WithFilter(filterBuilder ConditionBuilder) Builder {
  229. if b.expressionMap == nil {
  230. b.expressionMap = map[expressionType]treeBuilder{}
  231. }
  232. b.expressionMap[filter] = filterBuilder
  233. return b
  234. }
  235. // WithUpdate method adds the argument UpdateBuilder as an Update Expression
  236. // to the argument Builder. If the argument Builder already has a UpdateBuilder
  237. // representing a Update Expression, WithUpdate() overwrites the existing
  238. // UpdateBuilder.
  239. //
  240. // Example:
  241. //
  242. // // let builder be an existing Builder{} and update be an existing
  243. // // UpdateBuilder{}
  244. // builder = builder.WithUpdate(update)
  245. //
  246. // // add other DynamoDB Expressions to the builder. let cond be an already
  247. // // existing ConditionBuilder
  248. // builder = builder.WithCondition(cond)
  249. // // create an Expression struct
  250. // expr := builder.Build()
  251. func (b Builder) WithUpdate(updateBuilder UpdateBuilder) Builder {
  252. if b.expressionMap == nil {
  253. b.expressionMap = map[expressionType]treeBuilder{}
  254. }
  255. b.expressionMap[update] = updateBuilder
  256. return b
  257. }
  258. // Expression represents a collection of DynamoDB Expressions. The getter
  259. // methods of the Expression struct retrieves the formatted DynamoDB
  260. // Expressions, ExpressionAttributeNames, and ExpressionAttributeValues.
  261. //
  262. // Example:
  263. //
  264. // // keyCond represents the Key Condition Expression
  265. // keyCond := expression.Key("someKey").Equal(expression.Value("someValue"))
  266. // // proj represents the Projection Expression
  267. // proj := expression.NamesList(expression.Name("aName"), expression.Name("anotherName"), expression.Name("oneOtherName"))
  268. //
  269. // // Add keyCond and proj to builder as a Key Condition and Projection
  270. // // respectively
  271. // builder := expression.NewBuilder().WithKeyCondition(keyCond).WithProjection(proj)
  272. // expr := builder.Build()
  273. //
  274. // queryInput := dynamodb.QueryInput{
  275. // KeyConditionExpression: expr.KeyCondition(),
  276. // ProjectionExpression: expr.Projection(),
  277. // ExpressionAttributeNames: expr.Names(),
  278. // ExpressionAttributeValues: expr.Values(),
  279. // TableName: aws.String("SomeTable"),
  280. // }
  281. type Expression struct {
  282. expressionMap map[expressionType]string
  283. namesMap map[string]*string
  284. valuesMap map[string]*dynamodb.AttributeValue
  285. }
  286. // treeBuilder interface is fulfilled by builder structs that represent
  287. // different types of Expressions.
  288. type treeBuilder interface {
  289. // buildTree creates the tree structure of exprNodes. The tree structure
  290. // of exprNodes are traversed in order to build the string representing
  291. // different types of Expressions as well as the maps that represent
  292. // ExpressionAttributeNames and ExpressionAttributeValues.
  293. buildTree() (exprNode, error)
  294. }
  295. // Condition returns the *string corresponding to the Condition Expression
  296. // of the argument Expression. This method is used to satisfy the members of
  297. // DynamoDB input structs. If the Expression does not have a condition
  298. // expression this method returns nil.
  299. //
  300. // Example:
  301. //
  302. // // let expression be an instance of Expression{}
  303. //
  304. // deleteInput := dynamodb.DeleteItemInput{
  305. // ConditionExpression: expression.Condition(),
  306. // ExpressionAttributeNames: expression.Names(),
  307. // ExpressionAttributeValues: expression.Values(),
  308. // Key: map[string]*dynamodb.AttributeValue{
  309. // "PartitionKey": &dynamodb.AttributeValue{
  310. // S: aws.String("SomeKey"),
  311. // },
  312. // },
  313. // TableName: aws.String("SomeTable"),
  314. // }
  315. func (e Expression) Condition() *string {
  316. return e.returnExpression(condition)
  317. }
  318. // Filter returns the *string corresponding to the Filter Expression of the
  319. // argument Expression. This method is used to satisfy the members of DynamoDB
  320. // input structs. If the Expression does not have a filter expression this
  321. // method returns nil.
  322. //
  323. // Example:
  324. //
  325. // // let expression be an instance of Expression{}
  326. //
  327. // queryInput := dynamodb.QueryInput{
  328. // KeyConditionExpression: expression.KeyCondition(),
  329. // FilterExpression: expression.Filter(),
  330. // ExpressionAttributeNames: expression.Names(),
  331. // ExpressionAttributeValues: expression.Values(),
  332. // TableName: aws.String("SomeTable"),
  333. // }
  334. func (e Expression) Filter() *string {
  335. return e.returnExpression(filter)
  336. }
  337. // Projection returns the *string corresponding to the Projection Expression
  338. // of the argument Expression. This method is used to satisfy the members of
  339. // DynamoDB input structs. If the Expression does not have a projection
  340. // expression this method returns nil.
  341. //
  342. // Example:
  343. //
  344. // // let expression be an instance of Expression{}
  345. //
  346. // queryInput := dynamodb.QueryInput{
  347. // KeyConditionExpression: expression.KeyCondition(),
  348. // ProjectionExpression: expression.Projection(),
  349. // ExpressionAttributeNames: expression.Names(),
  350. // ExpressionAttributeValues: expression.Values(),
  351. // TableName: aws.String("SomeTable"),
  352. // }
  353. func (e Expression) Projection() *string {
  354. return e.returnExpression(projection)
  355. }
  356. // KeyCondition returns the *string corresponding to the Key Condition
  357. // Expression of the argument Expression. This method is used to satisfy the
  358. // members of DynamoDB input structs. If the argument Expression does not have a
  359. // KeyConditionExpression, KeyCondition() returns nil.
  360. //
  361. // Example:
  362. //
  363. // // let expression be an instance of Expression{}
  364. //
  365. // queryInput := dynamodb.QueryInput{
  366. // KeyConditionExpression: expression.KeyCondition(),
  367. // ProjectionExpression: expression.Projection(),
  368. // ExpressionAttributeNames: expression.Names(),
  369. // ExpressionAttributeValues: expression.Values(),
  370. // TableName: aws.String("SomeTable"),
  371. // }
  372. func (e Expression) KeyCondition() *string {
  373. return e.returnExpression(keyCondition)
  374. }
  375. // Update returns the *string corresponding to the Update Expression of the
  376. // argument Expression. This method is used to satisfy the members of DynamoDB
  377. // input structs. If the argument Expression does not have a UpdateExpression,
  378. // Update() returns nil.
  379. //
  380. // Example:
  381. //
  382. // // let expression be an instance of Expression{}
  383. //
  384. // updateInput := dynamodb.UpdateInput{
  385. // Key: map[string]*dynamodb.AttributeValue{
  386. // "PartitionKey": {
  387. // S: aws.String("someKey"),
  388. // },
  389. // },
  390. // UpdateExpression: expression.Update(),
  391. // ExpressionAttributeNames: expression.Names(),
  392. // ExpressionAttributeValues: expression.Values(),
  393. // TableName: aws.String("SomeTable"),
  394. // }
  395. func (e Expression) Update() *string {
  396. return e.returnExpression(update)
  397. }
  398. // Names returns the map[string]*string corresponding to the
  399. // ExpressionAttributeNames of the argument Expression. This method is used to
  400. // satisfy the members of DynamoDB input structs. If Expression does not use
  401. // ExpressionAttributeNames, this method returns nil. The
  402. // ExpressionAttributeNames and ExpressionAttributeValues member of the input
  403. // struct must always be assigned when using the Expression struct since all
  404. // item attribute names and values are aliased. That means that if the
  405. // ExpressionAttributeNames and ExpressionAttributeValues member is not assigned
  406. // with the corresponding Names() and Values() methods, the DynamoDB operation
  407. // will run into a logic error.
  408. //
  409. // Example:
  410. //
  411. // // let expression be an instance of Expression{}
  412. //
  413. // queryInput := dynamodb.QueryInput{
  414. // KeyConditionExpression: expression.KeyCondition(),
  415. // ProjectionExpression: expression.Projection(),
  416. // ExpressionAttributeNames: expression.Names(),
  417. // ExpressionAttributeValues: expression.Values(),
  418. // TableName: aws.String("SomeTable"),
  419. // }
  420. func (e Expression) Names() map[string]*string {
  421. return e.namesMap
  422. }
  423. // Values returns the map[string]*dynamodb.AttributeValue corresponding to
  424. // the ExpressionAttributeValues of the argument Expression. This method is used
  425. // to satisfy the members of DynamoDB input structs. If Expression does not use
  426. // ExpressionAttributeValues, this method returns nil. The
  427. // ExpressionAttributeNames and ExpressionAttributeValues member of the input
  428. // struct must always be assigned when using the Expression struct since all
  429. // item attribute names and values are aliased. That means that if the
  430. // ExpressionAttributeNames and ExpressionAttributeValues member is not assigned
  431. // with the corresponding Names() and Values() methods, the DynamoDB operation
  432. // will run into a logic error.
  433. //
  434. // Example:
  435. //
  436. // // let expression be an instance of Expression{}
  437. //
  438. // queryInput := dynamodb.QueryInput{
  439. // KeyConditionExpression: expression.KeyCondition(),
  440. // ProjectionExpression: expression.Projection(),
  441. // ExpressionAttributeNames: expression.Names(),
  442. // ExpressionAttributeValues: expression.Values(),
  443. // TableName: aws.String("SomeTable"),
  444. // }
  445. func (e Expression) Values() map[string]*dynamodb.AttributeValue {
  446. return e.valuesMap
  447. }
  448. // returnExpression returns *string corresponding to the type of Expression
  449. // string specified by the expressionType. If there is no corresponding
  450. // expression available in Expression, the method returns nil
  451. func (e Expression) returnExpression(expressionType expressionType) *string {
  452. if e.expressionMap == nil {
  453. return nil
  454. }
  455. if s, exists := e.expressionMap[expressionType]; exists {
  456. return &s
  457. }
  458. return nil
  459. }
  460. // exprNode are the generic nodes that represents both Operands and
  461. // Conditions. The purpose of exprNode is to be able to call an generic
  462. // recursive function on the top level exprNode to be able to determine a root
  463. // node in order to deduplicate name aliases.
  464. // fmtExpr is a string that has escaped characters to refer to
  465. // names/values/children which needs to be aliased at runtime in order to avoid
  466. // duplicate values. The rules are as follows:
  467. // $n: Indicates that an alias of a name needs to be inserted. The
  468. // corresponding name to be alias is in the []names slice.
  469. // $v: Indicates that an alias of a value needs to be inserted. The
  470. // corresponding value to be alias is in the []values slice.
  471. // $c: Indicates that the fmtExpr of a child exprNode needs to be inserted.
  472. // The corresponding child node is in the []children slice.
  473. type exprNode struct {
  474. names []string
  475. values []dynamodb.AttributeValue
  476. children []exprNode
  477. fmtExpr string
  478. }
  479. // aliasList keeps track of all the names we need to alias in the nested
  480. // struct of conditions and operands. This allows each alias to be unique.
  481. // aliasList is passed in as a pointer when buildChildTrees is called in
  482. // order to deduplicate all names within the tree strcuture of the exprNodes.
  483. type aliasList struct {
  484. namesList []string
  485. valuesList []dynamodb.AttributeValue
  486. }
  487. // buildExpressionString returns a string with aliasing for names/values
  488. // specified by aliasList. The string corresponds to the expression that the
  489. // exprNode tree represents.
  490. func (en exprNode) buildExpressionString(aliasList *aliasList) (string, error) {
  491. // Since each exprNode contains a slice of names, values, and children that
  492. // correspond to the escaped characters, we an index to traverse the slices
  493. index := struct {
  494. name, value, children int
  495. }{}
  496. formattedExpression := en.fmtExpr
  497. for i := 0; i < len(formattedExpression); {
  498. if formattedExpression[i] != '$' {
  499. i++
  500. continue
  501. }
  502. if i == len(formattedExpression)-1 {
  503. return "", fmt.Errorf("buildexprNode error: invalid escape character")
  504. }
  505. var alias string
  506. var err error
  507. // if an escaped character is found, substitute it with the proper alias
  508. // TODO consider AST instead of string in the future
  509. switch formattedExpression[i+1] {
  510. case 'n':
  511. alias, err = substitutePath(index.name, en, aliasList)
  512. if err != nil {
  513. return "", err
  514. }
  515. index.name++
  516. case 'v':
  517. alias, err = substituteValue(index.value, en, aliasList)
  518. if err != nil {
  519. return "", err
  520. }
  521. index.value++
  522. case 'c':
  523. alias, err = substituteChild(index.children, en, aliasList)
  524. if err != nil {
  525. return "", err
  526. }
  527. index.children++
  528. default:
  529. return "", fmt.Errorf("buildexprNode error: invalid escape rune %#v", formattedExpression[i+1])
  530. }
  531. formattedExpression = formattedExpression[:i] + alias + formattedExpression[i+2:]
  532. i += len(alias)
  533. }
  534. return formattedExpression, nil
  535. }
  536. // substitutePath substitutes the escaped character $n with the appropriate
  537. // alias.
  538. func substitutePath(index int, node exprNode, aliasList *aliasList) (string, error) {
  539. if index >= len(node.names) {
  540. return "", fmt.Errorf("substitutePath error: exprNode []names out of range")
  541. }
  542. str, err := aliasList.aliasPath(node.names[index])
  543. if err != nil {
  544. return "", err
  545. }
  546. return str, nil
  547. }
  548. // substituteValue substitutes the escaped character $v with the appropriate
  549. // alias.
  550. func substituteValue(index int, node exprNode, aliasList *aliasList) (string, error) {
  551. if index >= len(node.values) {
  552. return "", fmt.Errorf("substituteValue error: exprNode []values out of range")
  553. }
  554. str, err := aliasList.aliasValue(node.values[index])
  555. if err != nil {
  556. return "", err
  557. }
  558. return str, nil
  559. }
  560. // substituteChild substitutes the escaped character $c with the appropriate
  561. // alias.
  562. func substituteChild(index int, node exprNode, aliasList *aliasList) (string, error) {
  563. if index >= len(node.children) {
  564. return "", fmt.Errorf("substituteChild error: exprNode []children out of range")
  565. }
  566. return node.children[index].buildExpressionString(aliasList)
  567. }
  568. // aliasValue returns the corresponding alias to the dav value argument. Since
  569. // values are not deduplicated as of now, all values are just appended to the
  570. // aliasList and given the index as the alias.
  571. func (al *aliasList) aliasValue(dav dynamodb.AttributeValue) (string, error) {
  572. al.valuesList = append(al.valuesList, dav)
  573. return fmt.Sprintf(":%d", len(al.valuesList)-1), nil
  574. }
  575. // aliasPath returns the corresponding alias to the argument string. The
  576. // argument is checked against all existing aliasList names in order to avoid
  577. // duplicate strings getting two different aliases.
  578. func (al *aliasList) aliasPath(nm string) (string, error) {
  579. for ind, name := range al.namesList {
  580. if nm == name {
  581. return fmt.Sprintf("#%d", ind), nil
  582. }
  583. }
  584. al.namesList = append(al.namesList, nm)
  585. return fmt.Sprintf("#%d", len(al.namesList)-1), nil
  586. }