alias_timestamp_integration_test.go 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. package engine
  2. import (
  3. "strconv"
  4. "testing"
  5. "github.com/seaweedfs/seaweedfs/weed/pb/schema_pb"
  6. "github.com/stretchr/testify/assert"
  7. )
  8. // TestAliasTimestampIntegration tests that SQL aliases work correctly with timestamp query fixes
  9. func TestAliasTimestampIntegration(t *testing.T) {
  10. engine := NewTestSQLEngine()
  11. // Use the exact timestamps from the original failing production queries
  12. originalFailingTimestamps := []int64{
  13. 1756947416566456262, // Original failing query 1
  14. 1756947416566439304, // Original failing query 2
  15. 1756913789829292386, // Current data timestamp
  16. }
  17. t.Run("AliasWithLargeTimestamps", func(t *testing.T) {
  18. for i, timestamp := range originalFailingTimestamps {
  19. t.Run("Timestamp_"+strconv.Itoa(i+1), func(t *testing.T) {
  20. // Create test record
  21. testRecord := &schema_pb.RecordValue{
  22. Fields: map[string]*schema_pb.Value{
  23. "_timestamp_ns": {Kind: &schema_pb.Value_Int64Value{Int64Value: timestamp}},
  24. "id": {Kind: &schema_pb.Value_Int64Value{Int64Value: int64(1000 + i)}},
  25. },
  26. }
  27. // Test equality with alias (this was the originally failing pattern)
  28. sql := "SELECT _timestamp_ns AS ts, id FROM test WHERE ts = " + strconv.FormatInt(timestamp, 10)
  29. stmt, err := ParseSQL(sql)
  30. assert.NoError(t, err, "Should parse alias equality query for timestamp %d", timestamp)
  31. selectStmt := stmt.(*SelectStatement)
  32. predicate, err := engine.buildPredicateWithContext(selectStmt.Where.Expr, selectStmt.SelectExprs)
  33. assert.NoError(t, err, "Should build predicate for large timestamp with alias")
  34. result := predicate(testRecord)
  35. assert.True(t, result, "Should match exact large timestamp using alias")
  36. // Test precision - off by 1 nanosecond should not match
  37. sqlOffBy1 := "SELECT _timestamp_ns AS ts, id FROM test WHERE ts = " + strconv.FormatInt(timestamp+1, 10)
  38. stmt2, err := ParseSQL(sqlOffBy1)
  39. assert.NoError(t, err)
  40. selectStmt2 := stmt2.(*SelectStatement)
  41. predicate2, err := engine.buildPredicateWithContext(selectStmt2.Where.Expr, selectStmt2.SelectExprs)
  42. assert.NoError(t, err)
  43. result2 := predicate2(testRecord)
  44. assert.False(t, result2, "Should not match timestamp off by 1 nanosecond with alias")
  45. })
  46. }
  47. })
  48. t.Run("AliasWithTimestampRangeQueries", func(t *testing.T) {
  49. timestamp := int64(1756947416566456262)
  50. testRecords := []*schema_pb.RecordValue{
  51. {
  52. Fields: map[string]*schema_pb.Value{
  53. "_timestamp_ns": {Kind: &schema_pb.Value_Int64Value{Int64Value: timestamp - 2}}, // Before range
  54. },
  55. },
  56. {
  57. Fields: map[string]*schema_pb.Value{
  58. "_timestamp_ns": {Kind: &schema_pb.Value_Int64Value{Int64Value: timestamp}}, // In range
  59. },
  60. },
  61. {
  62. Fields: map[string]*schema_pb.Value{
  63. "_timestamp_ns": {Kind: &schema_pb.Value_Int64Value{Int64Value: timestamp + 2}}, // After range
  64. },
  65. },
  66. }
  67. // Test range query with alias
  68. sql := "SELECT _timestamp_ns AS ts FROM test WHERE ts >= " +
  69. strconv.FormatInt(timestamp-1, 10) + " AND ts <= " +
  70. strconv.FormatInt(timestamp+1, 10)
  71. stmt, err := ParseSQL(sql)
  72. assert.NoError(t, err, "Should parse range query with alias")
  73. selectStmt := stmt.(*SelectStatement)
  74. predicate, err := engine.buildPredicateWithContext(selectStmt.Where.Expr, selectStmt.SelectExprs)
  75. assert.NoError(t, err, "Should build range predicate with alias")
  76. // Test each record
  77. assert.False(t, predicate(testRecords[0]), "Should not match record before range")
  78. assert.True(t, predicate(testRecords[1]), "Should match record in range")
  79. assert.False(t, predicate(testRecords[2]), "Should not match record after range")
  80. })
  81. t.Run("AliasWithTimestampPrecisionEdgeCases", func(t *testing.T) {
  82. // Test maximum int64 value
  83. maxInt64 := int64(9223372036854775807)
  84. testRecord := &schema_pb.RecordValue{
  85. Fields: map[string]*schema_pb.Value{
  86. "_timestamp_ns": {Kind: &schema_pb.Value_Int64Value{Int64Value: maxInt64}},
  87. },
  88. }
  89. // Test with alias
  90. sql := "SELECT _timestamp_ns AS ts FROM test WHERE ts = " + strconv.FormatInt(maxInt64, 10)
  91. stmt, err := ParseSQL(sql)
  92. assert.NoError(t, err, "Should parse max int64 with alias")
  93. selectStmt := stmt.(*SelectStatement)
  94. predicate, err := engine.buildPredicateWithContext(selectStmt.Where.Expr, selectStmt.SelectExprs)
  95. assert.NoError(t, err, "Should build predicate for max int64 with alias")
  96. result := predicate(testRecord)
  97. assert.True(t, result, "Should handle max int64 value correctly with alias")
  98. // Test minimum value
  99. minInt64 := int64(-9223372036854775808)
  100. testRecord2 := &schema_pb.RecordValue{
  101. Fields: map[string]*schema_pb.Value{
  102. "_timestamp_ns": {Kind: &schema_pb.Value_Int64Value{Int64Value: minInt64}},
  103. },
  104. }
  105. sql2 := "SELECT _timestamp_ns AS ts FROM test WHERE ts = " + strconv.FormatInt(minInt64, 10)
  106. stmt2, err := ParseSQL(sql2)
  107. assert.NoError(t, err)
  108. selectStmt2 := stmt2.(*SelectStatement)
  109. predicate2, err := engine.buildPredicateWithContext(selectStmt2.Where.Expr, selectStmt2.SelectExprs)
  110. assert.NoError(t, err)
  111. result2 := predicate2(testRecord2)
  112. assert.True(t, result2, "Should handle min int64 value correctly with alias")
  113. })
  114. t.Run("MultipleAliasesWithTimestamps", func(t *testing.T) {
  115. // Test multiple aliases including timestamps
  116. timestamp1 := int64(1756947416566456262)
  117. timestamp2 := int64(1756913789829292386)
  118. testRecord := &schema_pb.RecordValue{
  119. Fields: map[string]*schema_pb.Value{
  120. "_timestamp_ns": {Kind: &schema_pb.Value_Int64Value{Int64Value: timestamp1}},
  121. "created_at": {Kind: &schema_pb.Value_Int64Value{Int64Value: timestamp2}},
  122. "id": {Kind: &schema_pb.Value_Int64Value{Int64Value: 12345}},
  123. },
  124. }
  125. // Use multiple timestamp aliases in WHERE
  126. sql := "SELECT _timestamp_ns AS event_time, created_at AS created_time, id AS record_id FROM test " +
  127. "WHERE event_time = " + strconv.FormatInt(timestamp1, 10) +
  128. " AND created_time = " + strconv.FormatInt(timestamp2, 10) +
  129. " AND record_id = 12345"
  130. stmt, err := ParseSQL(sql)
  131. assert.NoError(t, err, "Should parse complex query with multiple timestamp aliases")
  132. selectStmt := stmt.(*SelectStatement)
  133. predicate, err := engine.buildPredicateWithContext(selectStmt.Where.Expr, selectStmt.SelectExprs)
  134. assert.NoError(t, err, "Should build predicate for multiple timestamp aliases")
  135. result := predicate(testRecord)
  136. assert.True(t, result, "Should match complex query with multiple timestamp aliases")
  137. })
  138. t.Run("CompatibilityWithExistingTimestampFixes", func(t *testing.T) {
  139. // Verify that all the timestamp fixes (precision, scan boundaries, etc.) still work with aliases
  140. largeTimestamp := int64(1756947416566456262)
  141. // Test all comparison operators with aliases
  142. operators := []struct {
  143. sql string
  144. value int64
  145. expected bool
  146. }{
  147. {"ts = " + strconv.FormatInt(largeTimestamp, 10), largeTimestamp, true},
  148. {"ts = " + strconv.FormatInt(largeTimestamp+1, 10), largeTimestamp, false},
  149. {"ts > " + strconv.FormatInt(largeTimestamp-1, 10), largeTimestamp, true},
  150. {"ts > " + strconv.FormatInt(largeTimestamp, 10), largeTimestamp, false},
  151. {"ts >= " + strconv.FormatInt(largeTimestamp, 10), largeTimestamp, true},
  152. {"ts >= " + strconv.FormatInt(largeTimestamp+1, 10), largeTimestamp, false},
  153. {"ts < " + strconv.FormatInt(largeTimestamp+1, 10), largeTimestamp, true},
  154. {"ts < " + strconv.FormatInt(largeTimestamp, 10), largeTimestamp, false},
  155. {"ts <= " + strconv.FormatInt(largeTimestamp, 10), largeTimestamp, true},
  156. {"ts <= " + strconv.FormatInt(largeTimestamp-1, 10), largeTimestamp, false},
  157. }
  158. for _, op := range operators {
  159. t.Run(op.sql, func(t *testing.T) {
  160. testRecord := &schema_pb.RecordValue{
  161. Fields: map[string]*schema_pb.Value{
  162. "_timestamp_ns": {Kind: &schema_pb.Value_Int64Value{Int64Value: op.value}},
  163. },
  164. }
  165. sql := "SELECT _timestamp_ns AS ts FROM test WHERE " + op.sql
  166. stmt, err := ParseSQL(sql)
  167. assert.NoError(t, err, "Should parse: %s", op.sql)
  168. selectStmt := stmt.(*SelectStatement)
  169. predicate, err := engine.buildPredicateWithContext(selectStmt.Where.Expr, selectStmt.SelectExprs)
  170. assert.NoError(t, err, "Should build predicate for: %s", op.sql)
  171. result := predicate(testRecord)
  172. assert.Equal(t, op.expected, result, "Alias operator test failed for: %s", op.sql)
  173. })
  174. }
  175. })
  176. t.Run("ProductionScenarioReproduction", func(t *testing.T) {
  177. // Reproduce the exact production scenario that was originally failing
  178. // This was the original failing pattern from the user
  179. originalFailingSQL := "select id, _timestamp_ns as ts from ecommerce.user_events where ts = 1756913789829292386"
  180. testRecord := &schema_pb.RecordValue{
  181. Fields: map[string]*schema_pb.Value{
  182. "_timestamp_ns": {Kind: &schema_pb.Value_Int64Value{Int64Value: 1756913789829292386}},
  183. "id": {Kind: &schema_pb.Value_Int64Value{Int64Value: 82460}},
  184. },
  185. }
  186. stmt, err := ParseSQL(originalFailingSQL)
  187. assert.NoError(t, err, "Should parse the exact originally failing production query")
  188. selectStmt := stmt.(*SelectStatement)
  189. predicate, err := engine.buildPredicateWithContext(selectStmt.Where.Expr, selectStmt.SelectExprs)
  190. assert.NoError(t, err, "Should build predicate for original failing query")
  191. result := predicate(testRecord)
  192. assert.True(t, result, "The originally failing production query should now work perfectly")
  193. // Also test the other originally failing timestamp
  194. originalFailingSQL2 := "select id, _timestamp_ns as ts from ecommerce.user_events where ts = 1756947416566456262"
  195. testRecord2 := &schema_pb.RecordValue{
  196. Fields: map[string]*schema_pb.Value{
  197. "_timestamp_ns": {Kind: &schema_pb.Value_Int64Value{Int64Value: 1756947416566456262}},
  198. "id": {Kind: &schema_pb.Value_Int64Value{Int64Value: 897795}},
  199. },
  200. }
  201. stmt2, err := ParseSQL(originalFailingSQL2)
  202. assert.NoError(t, err)
  203. selectStmt2 := stmt2.(*SelectStatement)
  204. predicate2, err := engine.buildPredicateWithContext(selectStmt2.Where.Expr, selectStmt2.SelectExprs)
  205. assert.NoError(t, err)
  206. result2 := predicate2(testRecord2)
  207. assert.True(t, result2, "The second originally failing production query should now work perfectly")
  208. })
  209. }