timestamp_query_fixes_test.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  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. // TestTimestampQueryFixes tests all the timestamp query fixes comprehensively
  9. func TestTimestampQueryFixes(t *testing.T) {
  10. engine := NewTestSQLEngine()
  11. // Test timestamps from the actual failing cases
  12. largeTimestamp1 := int64(1756947416566456262) // Original failing query
  13. largeTimestamp2 := int64(1756947416566439304) // Second failing query
  14. largeTimestamp3 := int64(1756913789829292386) // Current data timestamp
  15. t.Run("Fix1_PrecisionLoss", func(t *testing.T) {
  16. // Test that large int64 timestamps don't lose precision in comparisons
  17. testRecord := &schema_pb.RecordValue{
  18. Fields: map[string]*schema_pb.Value{
  19. "_timestamp_ns": {Kind: &schema_pb.Value_Int64Value{Int64Value: largeTimestamp1}},
  20. "id": {Kind: &schema_pb.Value_Int64Value{Int64Value: 12345}},
  21. },
  22. }
  23. // Test equality comparison
  24. result := engine.valuesEqual(testRecord.Fields["_timestamp_ns"], largeTimestamp1)
  25. assert.True(t, result, "Large timestamp equality should work without precision loss")
  26. // Test inequality comparison
  27. result = engine.valuesEqual(testRecord.Fields["_timestamp_ns"], largeTimestamp1+1)
  28. assert.False(t, result, "Large timestamp inequality should be detected accurately")
  29. // Test less than comparison
  30. result = engine.valueLessThan(testRecord.Fields["_timestamp_ns"], largeTimestamp1+1)
  31. assert.True(t, result, "Large timestamp less-than should work without precision loss")
  32. // Test greater than comparison
  33. result = engine.valueGreaterThan(testRecord.Fields["_timestamp_ns"], largeTimestamp1-1)
  34. assert.True(t, result, "Large timestamp greater-than should work without precision loss")
  35. })
  36. t.Run("Fix2_TimeFilterExtraction", func(t *testing.T) {
  37. // Test that equality queries don't set stopTimeNs (which causes premature termination)
  38. equalitySQL := "SELECT * FROM test WHERE _timestamp_ns = " + strconv.FormatInt(largeTimestamp2, 10)
  39. stmt, err := ParseSQL(equalitySQL)
  40. assert.NoError(t, err)
  41. selectStmt := stmt.(*SelectStatement)
  42. startTimeNs, stopTimeNs := engine.extractTimeFilters(selectStmt.Where.Expr)
  43. assert.Equal(t, largeTimestamp2-1, startTimeNs, "Equality query should set startTimeNs to target-1")
  44. assert.Equal(t, int64(0), stopTimeNs, "Equality query should NOT set stopTimeNs to avoid early termination")
  45. })
  46. t.Run("Fix3_RangeBoundaryFix", func(t *testing.T) {
  47. // Test that range queries with equal boundaries don't cause premature termination
  48. rangeSQL := "SELECT * FROM test WHERE _timestamp_ns >= " + strconv.FormatInt(largeTimestamp3, 10) +
  49. " AND _timestamp_ns <= " + strconv.FormatInt(largeTimestamp3, 10)
  50. stmt, err := ParseSQL(rangeSQL)
  51. assert.NoError(t, err)
  52. selectStmt := stmt.(*SelectStatement)
  53. startTimeNs, stopTimeNs := engine.extractTimeFilters(selectStmt.Where.Expr)
  54. // Should be treated like an equality query to avoid premature termination
  55. assert.NotEqual(t, int64(0), startTimeNs, "Range with equal boundaries should set startTimeNs")
  56. assert.Equal(t, int64(0), stopTimeNs, "Range with equal boundaries should NOT set stopTimeNs")
  57. })
  58. t.Run("Fix4_DifferentRangeBoundaries", func(t *testing.T) {
  59. // Test that normal range queries still work correctly
  60. rangeSQL := "SELECT * FROM test WHERE _timestamp_ns >= " + strconv.FormatInt(largeTimestamp1, 10) +
  61. " AND _timestamp_ns <= " + strconv.FormatInt(largeTimestamp2, 10)
  62. stmt, err := ParseSQL(rangeSQL)
  63. assert.NoError(t, err)
  64. selectStmt := stmt.(*SelectStatement)
  65. startTimeNs, stopTimeNs := engine.extractTimeFilters(selectStmt.Where.Expr)
  66. assert.Equal(t, largeTimestamp1, startTimeNs, "Range query should set correct startTimeNs")
  67. assert.Equal(t, largeTimestamp2, stopTimeNs, "Range query should set correct stopTimeNs")
  68. })
  69. t.Run("Fix5_PredicateAccuracy", func(t *testing.T) {
  70. // Test that predicates correctly evaluate large timestamp equality
  71. equalitySQL := "SELECT * FROM test WHERE _timestamp_ns = " + strconv.FormatInt(largeTimestamp1, 10)
  72. stmt, err := ParseSQL(equalitySQL)
  73. assert.NoError(t, err)
  74. selectStmt := stmt.(*SelectStatement)
  75. predicate, err := engine.buildPredicate(selectStmt.Where.Expr)
  76. assert.NoError(t, err)
  77. // Test with matching record
  78. matchingRecord := &schema_pb.RecordValue{
  79. Fields: map[string]*schema_pb.Value{
  80. "_timestamp_ns": {Kind: &schema_pb.Value_Int64Value{Int64Value: largeTimestamp1}},
  81. "id": {Kind: &schema_pb.Value_Int64Value{Int64Value: 897795}},
  82. },
  83. }
  84. result := predicate(matchingRecord)
  85. assert.True(t, result, "Predicate should match record with exact timestamp")
  86. // Test with non-matching record
  87. nonMatchingRecord := &schema_pb.RecordValue{
  88. Fields: map[string]*schema_pb.Value{
  89. "_timestamp_ns": {Kind: &schema_pb.Value_Int64Value{Int64Value: largeTimestamp1 + 1}},
  90. "id": {Kind: &schema_pb.Value_Int64Value{Int64Value: 12345}},
  91. },
  92. }
  93. result = predicate(nonMatchingRecord)
  94. assert.False(t, result, "Predicate should NOT match record with different timestamp")
  95. })
  96. t.Run("Fix6_ComparisonOperators", func(t *testing.T) {
  97. // Test all comparison operators work correctly with large timestamps
  98. testRecord := &schema_pb.RecordValue{
  99. Fields: map[string]*schema_pb.Value{
  100. "_timestamp_ns": {Kind: &schema_pb.Value_Int64Value{Int64Value: largeTimestamp2}},
  101. },
  102. }
  103. operators := []struct {
  104. sql string
  105. expected bool
  106. }{
  107. {"_timestamp_ns = " + strconv.FormatInt(largeTimestamp2, 10), true},
  108. {"_timestamp_ns = " + strconv.FormatInt(largeTimestamp2+1, 10), false},
  109. {"_timestamp_ns > " + strconv.FormatInt(largeTimestamp2-1, 10), true},
  110. {"_timestamp_ns > " + strconv.FormatInt(largeTimestamp2, 10), false},
  111. {"_timestamp_ns >= " + strconv.FormatInt(largeTimestamp2, 10), true},
  112. {"_timestamp_ns >= " + strconv.FormatInt(largeTimestamp2+1, 10), false},
  113. {"_timestamp_ns < " + strconv.FormatInt(largeTimestamp2+1, 10), true},
  114. {"_timestamp_ns < " + strconv.FormatInt(largeTimestamp2, 10), false},
  115. {"_timestamp_ns <= " + strconv.FormatInt(largeTimestamp2, 10), true},
  116. {"_timestamp_ns <= " + strconv.FormatInt(largeTimestamp2-1, 10), false},
  117. }
  118. for _, op := range operators {
  119. sql := "SELECT * FROM test WHERE " + op.sql
  120. stmt, err := ParseSQL(sql)
  121. assert.NoError(t, err, "Should parse SQL: %s", op.sql)
  122. selectStmt := stmt.(*SelectStatement)
  123. predicate, err := engine.buildPredicate(selectStmt.Where.Expr)
  124. assert.NoError(t, err, "Should build predicate for: %s", op.sql)
  125. result := predicate(testRecord)
  126. assert.Equal(t, op.expected, result, "Operator test failed for: %s", op.sql)
  127. }
  128. })
  129. t.Run("Fix7_EdgeCases", func(t *testing.T) {
  130. // Test edge cases and boundary conditions
  131. // Maximum int64 value
  132. maxInt64 := int64(9223372036854775807)
  133. testRecord := &schema_pb.RecordValue{
  134. Fields: map[string]*schema_pb.Value{
  135. "_timestamp_ns": {Kind: &schema_pb.Value_Int64Value{Int64Value: maxInt64}},
  136. },
  137. }
  138. // Test equality with maximum int64
  139. result := engine.valuesEqual(testRecord.Fields["_timestamp_ns"], maxInt64)
  140. assert.True(t, result, "Should handle maximum int64 value correctly")
  141. // Test with zero timestamp
  142. zeroRecord := &schema_pb.RecordValue{
  143. Fields: map[string]*schema_pb.Value{
  144. "_timestamp_ns": {Kind: &schema_pb.Value_Int64Value{Int64Value: 0}},
  145. },
  146. }
  147. result = engine.valuesEqual(zeroRecord.Fields["_timestamp_ns"], int64(0))
  148. assert.True(t, result, "Should handle zero timestamp correctly")
  149. })
  150. }
  151. // TestOriginalFailingQueries tests the specific queries that were failing before the fixes
  152. func TestOriginalFailingQueries(t *testing.T) {
  153. engine := NewTestSQLEngine()
  154. failingQueries := []struct {
  155. name string
  156. sql string
  157. timestamp int64
  158. id int64
  159. }{
  160. {
  161. name: "OriginalQuery1",
  162. sql: "select id, _timestamp_ns from ecommerce.user_events where _timestamp_ns = 1756947416566456262",
  163. timestamp: 1756947416566456262,
  164. id: 897795,
  165. },
  166. {
  167. name: "OriginalQuery2",
  168. sql: "select id, _timestamp_ns from ecommerce.user_events where _timestamp_ns = 1756947416566439304",
  169. timestamp: 1756947416566439304,
  170. id: 715356,
  171. },
  172. {
  173. name: "CurrentDataQuery",
  174. sql: "select id, _timestamp_ns from ecommerce.user_events where _timestamp_ns = 1756913789829292386",
  175. timestamp: 1756913789829292386,
  176. id: 82460,
  177. },
  178. }
  179. for _, query := range failingQueries {
  180. t.Run(query.name, func(t *testing.T) {
  181. // Parse the SQL
  182. stmt, err := ParseSQL(query.sql)
  183. assert.NoError(t, err, "Should parse the failing query")
  184. selectStmt := stmt.(*SelectStatement)
  185. // Test time filter extraction
  186. startTimeNs, stopTimeNs := engine.extractTimeFilters(selectStmt.Where.Expr)
  187. assert.Equal(t, query.timestamp-1, startTimeNs, "Should set startTimeNs to timestamp-1")
  188. assert.Equal(t, int64(0), stopTimeNs, "Should not set stopTimeNs for equality")
  189. // Test predicate building and evaluation
  190. predicate, err := engine.buildPredicate(selectStmt.Where.Expr)
  191. assert.NoError(t, err, "Should build predicate")
  192. // Test with matching record
  193. matchingRecord := &schema_pb.RecordValue{
  194. Fields: map[string]*schema_pb.Value{
  195. "_timestamp_ns": {Kind: &schema_pb.Value_Int64Value{Int64Value: query.timestamp}},
  196. "id": {Kind: &schema_pb.Value_Int64Value{Int64Value: query.id}},
  197. },
  198. }
  199. result := predicate(matchingRecord)
  200. assert.True(t, result, "Predicate should match the target record for query: %s", query.name)
  201. })
  202. }
  203. }