@@ -408,17 +408,15 @@ func firstPassScanAndUpdate(
408408
409409 updateExpr := setPrefix
410410 vals := map [string ]* dynamodb.AttributeValue {":empty" : {S : aws .String ("" )}}
411- names := map [string ]* string {
412- "#date_created" : aws .String (attrDateCreated ),
413- "#date_modified" : aws .String (attrDateModified ),
414- }
411+ names := map [string ]* string {}
415412 first := true
416413 if setCreated {
417414 if ! first {
418415 updateExpr += commaSep
419416 }
420417 updateExpr += exprSetDateCreated
421418 vals [":date_created" ] = & dynamodb.AttributeValue {S : aws .String (finalC )}
419+ names ["#date_created" ] = aws .String (attrDateCreated )
422420 first = false
423421 }
424422 if setModified {
@@ -427,6 +425,7 @@ func firstPassScanAndUpdate(
427425 }
428426 updateExpr += exprSetDateModified
429427 vals [":date_modified" ] = & dynamodb.AttributeValue {S : aws .String (finalM )}
428+ names ["#date_modified" ] = aws .String (attrDateModified )
430429 }
431430
432431 if debug {
@@ -450,7 +449,8 @@ func firstPassScanAndUpdate(
450449 }
451450
452451 // Build CLI (always emitted in dry-run; emitted on failure in live-run)
453- cmd := buildAwsCliUpdate (region , stage , tableName , sig .SignatureID , updateExpr , names , vals , condAnyMissing )
452+ condExpr := buildConditionExpression (names )
453+ cmd := buildAwsCliUpdate (region , stage , tableName , sig .SignatureID , updateExpr , names , vals , condExpr )
454454 dbg (" CLI: %s" , cmd )
455455
456456 if dryRun {
@@ -468,7 +468,7 @@ func firstPassScanAndUpdate(
468468 UpdateExpression : aws .String (updateExpr ),
469469 ExpressionAttributeNames : names ,
470470 ExpressionAttributeValues : vals ,
471- ConditionExpression : aws .String (condAnyMissing ),
471+ ConditionExpression : aws .String (condExpr ),
472472 })
473473 if uerr != nil {
474474 log .Printf ("Update failed %s: %v" , sig .SignatureID , uerr )
@@ -604,12 +604,7 @@ func snowflakeFix(
604604
605605 updateExpr := setPrefix
606606 vals := map [string ]* dynamodb.AttributeValue {":empty" : {S : aws .String ("" )}}
607- names := map [string ]* string {
608- "#date_created" : aws .String (attrDateCreated ),
609- "#date_modified" : aws .String (attrDateModified ),
610- "#approx_date_created" : aws .String (attrApproxDateCreated ),
611- "#approx_date_modified" : aws .String (attrApproxDateModified ),
612- }
607+ names := map [string ]* string {}
613608 first := true
614609 if setCreated {
615610 if ! first {
@@ -619,9 +614,11 @@ func snowflakeFix(
619614 if srcC == labelFivetranSynced {
620615 updateExpr += exprSetApproxDateCreated
621616 vals [":approx_date_created" ] = & dynamodb.AttributeValue {S : aws .String (finalC )}
617+ names ["#approx_date_created" ] = aws .String (attrApproxDateCreated )
622618 } else {
623619 updateExpr += exprSetDateCreated
624620 vals [":date_created" ] = & dynamodb.AttributeValue {S : aws .String (finalC )}
621+ names ["#date_created" ] = aws .String (attrDateCreated )
625622 }
626623 first = false
627624 }
@@ -633,9 +630,11 @@ func snowflakeFix(
633630 if srcM == labelFivetranSynced {
634631 updateExpr += exprSetApproxDateModified
635632 vals [":approx_date_modified" ] = & dynamodb.AttributeValue {S : aws .String (finalM )}
633+ names ["#approx_date_modified" ] = aws .String (attrApproxDateModified )
636634 } else {
637635 updateExpr += exprSetDateModified
638636 vals [":date_modified" ] = & dynamodb.AttributeValue {S : aws .String (finalM )}
637+ names ["#date_modified" ] = aws .String (attrDateModified )
639638 }
640639 }
641640
@@ -659,7 +658,9 @@ func snowflakeFix(
659658 }
660659 }
661660
662- cmd := buildAwsCliUpdate (region , stage , tableName , id , updateExpr , names , vals , condAnyMissing )
661+ // Build condition expression with names that are actually defined
662+ condExpr := buildConditionExpression (names )
663+ cmd := buildAwsCliUpdate (region , stage , tableName , id , updateExpr , names , vals , condExpr )
663664 dbg (" SF CLI: %s" , cmd )
664665
665666 if dryRun {
@@ -672,13 +673,16 @@ func snowflakeFix(
672673 continue
673674 }
674675
676+ // Build condition expression with names that are actually defined
677+ condExpr = buildConditionExpression (names )
678+
675679 _ , uerr := ddb .UpdateItem (& dynamodb.UpdateItemInput {
676680 TableName : aws .String (tableName ),
677681 Key : map [string ]* dynamodb.AttributeValue {"signature_id" : {S : aws .String (id )}},
678682 UpdateExpression : aws .String (updateExpr ),
679683 ExpressionAttributeNames : names ,
680684 ExpressionAttributeValues : vals ,
681- ConditionExpression : aws .String (condAnyMissing ),
685+ ConditionExpression : aws .String (condExpr ),
682686 })
683687 if uerr != nil {
684688 log .Printf ("Update failed (SF) %s: %v" , id , uerr )
@@ -1058,6 +1062,39 @@ func parseSnowflakeCSV(b []byte) map[string]string {
10581062 return res
10591063}
10601064
1065+ // buildConditionExpression builds a condition expression using only the attribute names
1066+ // that are actually defined in the names map to avoid DynamoDB validation errors
1067+ func buildConditionExpression (names map [string ]* string ) string {
1068+ var conditions []string
1069+
1070+ // Check for regular date_created field
1071+ if _ , hasDateCreated := names ["#date_created" ]; hasDateCreated {
1072+ conditions = append (conditions , "attribute_not_exists(#date_created) OR #date_created = :empty" )
1073+ }
1074+
1075+ // Check for regular date_modified field
1076+ if _ , hasDateModified := names ["#date_modified" ]; hasDateModified {
1077+ conditions = append (conditions , "attribute_not_exists(#date_modified) OR #date_modified = :empty" )
1078+ }
1079+
1080+ // Check for approx_date_created field
1081+ if _ , hasApproxDateCreated := names ["#approx_date_created" ]; hasApproxDateCreated {
1082+ conditions = append (conditions , "attribute_not_exists(#approx_date_created) OR #approx_date_created = :empty" )
1083+ }
1084+
1085+ // Check for approx_date_modified field
1086+ if _ , hasApproxDateModified := names ["#approx_date_modified" ]; hasApproxDateModified {
1087+ conditions = append (conditions , "attribute_not_exists(#approx_date_modified) OR #approx_date_modified = :empty" )
1088+ }
1089+
1090+ // If no specific conditions, use a basic condition that should always allow updates
1091+ if len (conditions ) == 0 {
1092+ return "attribute_exists(signature_id)"
1093+ }
1094+
1095+ return strings .Join (conditions , " OR " )
1096+ }
1097+
10611098// -----------------------------------------------------------------------------
10621099// AWS CLI builder & stats print
10631100// -----------------------------------------------------------------------------
0 commit comments