@@ -10,26 +10,10 @@ class ComputedColumnValidator
1010 * Allowed SQL functions.
1111 */
1212 protected array $ allowedFunctions = [
13+ // Date/Time Functions
1314 'DATEDIFF ' ,
1415 'DATE_ADD ' ,
1516 'DATE_SUB ' ,
16- 'CONCAT ' ,
17- 'COALESCE ' ,
18- 'IFNULL ' ,
19- 'CASE ' ,
20- 'WHEN ' ,
21- 'THEN ' ,
22- 'ELSE ' ,
23- 'END ' ,
24- 'LEAST ' ,
25- 'GREATEST ' ,
26- 'ROUND ' ,
27- 'ABS ' ,
28- 'UPPER ' ,
29- 'LOWER ' ,
30- 'TRIM ' ,
31- 'LENGTH ' ,
32- 'NULLIF ' ,
3317 'NOW ' ,
3418 'CURDATE ' ,
3519 'CURTIME ' ,
@@ -40,9 +24,103 @@ class ComputedColumnValidator
4024 'MINUTE ' ,
4125 'SECOND ' ,
4226 'DATE_FORMAT ' ,
27+ 'LAST_DAY ' , // Get last day of month
28+ 'DAYOFWEEK ' , // Get day of week (1=Sunday, 7=Saturday)
29+ 'DAYOFMONTH ' , // Get day of month (1-31)
30+ 'DAYOFYEAR ' , // Get day of year (1-366)
31+ 'WEEK ' , // Get week number
32+ 'WEEKDAY ' , // Get weekday index (0=Monday, 6=Sunday)
33+ 'QUARTER ' , // Get quarter (1-4)
34+ 'TIMESTAMPDIFF ' , // Difference between timestamps
35+ 'TIMESTAMPADD ' , // Add interval to timestamp
36+ 'FROM_UNIXTIME ' , // Convert Unix timestamp to datetime
37+ 'UNIX_TIMESTAMP ' , // Convert datetime to Unix timestamp
38+ 'STR_TO_DATE ' , // Parse string to date
39+ 'MAKEDATE ' , // Create date from year and day of year
40+ 'ADDDATE ' , // Add days to date
41+ 'SUBDATE ' , // Subtract days from date
42+
43+ // String Functions
44+ 'CONCAT ' ,
45+ 'CONCAT_WS ' , // Concat with separator
4346 'SUBSTRING ' ,
47+ 'SUBSTR ' , // Alias for SUBSTRING
4448 'REPLACE ' ,
45- 'INTERVAL ' ,
49+ 'UPPER ' ,
50+ 'LOWER ' ,
51+ 'TRIM ' ,
52+ 'LTRIM ' , // Left trim
53+ 'RTRIM ' , // Right trim
54+ 'LENGTH ' ,
55+ 'CHAR_LENGTH ' , // Character length
56+ 'LEFT ' , // Get leftmost characters
57+ 'RIGHT ' , // Get rightmost characters
58+ 'LPAD ' , // Left pad string
59+ 'RPAD ' , // Right pad string
60+ 'REVERSE ' , // Reverse string
61+ 'LOCATE ' , // Find substring position
62+ 'POSITION ' , // Find substring position
63+ 'INSTR ' , // Find substring position
64+ 'STRCMP ' , // Compare strings
65+
66+ // Numeric Functions
67+ 'ROUND ' ,
68+ 'ABS ' ,
69+ 'CEIL ' , // Round up
70+ 'CEILING ' , // Round up (alias)
71+ 'FLOOR ' , // Round down
72+ 'TRUNCATE ' , // Truncate to decimal places
73+ 'MOD ' , // Modulo
74+ 'POW ' , // Power
75+ 'POWER ' , // Power (alias)
76+ 'SQRT ' , // Square root
77+ 'SIGN ' , // Sign of number (-1, 0, 1)
78+ 'RAND ' , // Random number
79+ 'PI ' , // Pi constant
80+ 'EXP ' , // Exponential
81+ 'LN ' , // Natural logarithm
82+ 'LOG ' , // Logarithm
83+ 'LOG10 ' , // Base-10 logarithm
84+ 'LOG2 ' , // Base-2 logarithm
85+ 'DEGREES ' , // Radians to degrees
86+ 'RADIANS ' , // Degrees to radians
87+ 'SIN ' , // Sine
88+ 'COS ' , // Cosine
89+ 'TAN ' , // Tangent
90+ 'ASIN ' , // Arc sine
91+ 'ACOS ' , // Arc cosine
92+ 'ATAN ' , // Arc tangent
93+ 'ATAN2 ' , // Arc tangent of two variables
94+
95+ // Conditional/Logic Functions
96+ 'CASE ' ,
97+ 'WHEN ' ,
98+ 'THEN ' ,
99+ 'ELSE ' ,
100+ 'END ' ,
101+ 'IF ' , // IF(condition, true_value, false_value)
102+ 'IFNULL ' ,
103+ 'NULLIF ' ,
104+ 'COALESCE ' ,
105+
106+ // Comparison Functions
107+ 'LEAST ' ,
108+ 'GREATEST ' ,
109+
110+ // Aggregate Functions (for reference, though typically used in GROUP BY)
111+ 'COUNT ' ,
112+ 'SUM ' ,
113+ 'AVG ' ,
114+ 'MIN ' ,
115+ 'MAX ' ,
116+ 'GROUP_CONCAT ' ,
117+
118+ // Type Conversion
119+ 'CAST ' ,
120+ 'CONVERT ' ,
121+
122+ // Other Utility Functions
123+ 'INTERVAL ' , // For date arithmetic
46124 ];
47125
48126 /**
@@ -78,10 +156,11 @@ public function __construct(ReportSchemaRegistry $registry)
78156 *
79157 * @param string $expression the SQL expression
80158 * @param string $tableName the base table name for column validation
159+ * @param array $computedColumns optional array of other computed columns that can be referenced
81160 *
82161 * @return array validation result with 'valid' boolean and optional 'errors' array
83162 */
84- public function validate (string $ expression , string $ tableName ): array
163+ public function validate (string $ expression , string $ tableName, array $ computedColumns = [] ): array
85164 {
86165 $ errors = [];
87166
@@ -104,7 +183,7 @@ public function validate(string $expression, string $tableName): array
104183 }
105184
106185 // Validate column references
107- $ columnCheck = $ this ->validateColumnReferences ($ expression , $ tableName );
186+ $ columnCheck = $ this ->validateColumnReferences ($ expression , $ tableName, $ computedColumns );
108187 if (!$ columnCheck ['valid ' ]) {
109188 $ errors = array_merge ($ errors , $ columnCheck ['errors ' ]);
110189 }
@@ -188,7 +267,7 @@ protected function validateOperators(string $expression): array
188267 /**
189268 * Validate that all column references exist in the schema.
190269 */
191- protected function validateColumnReferences (string $ expression , string $ tableName ): array
270+ protected function validateColumnReferences (string $ expression , string $ tableName, array $ computedColumns = [] ): array
192271 {
193272 $ errors = [];
194273
@@ -201,6 +280,12 @@ protected function validateColumnReferences(string $expression, string $tableNam
201280 ];
202281 }
203282
283+ // Build a list of computed column names for quick lookup
284+ $ computedColumnNames = [];
285+ foreach ($ computedColumns as $ col ) {
286+ $ computedColumnNames [] = $ col ['name ' ] ?? '' ;
287+ }
288+
204289 // Remove string literals (both single and double quoted) to avoid treating them as column references
205290 $ cleanExpression = $ this ->removeStringLiterals ($ expression );
206291
@@ -217,6 +302,11 @@ protected function validateColumnReferences(string $expression, string $tableNam
217302 continue ;
218303 }
219304
305+ // Check if it's a reference to another computed column
306+ if (in_array ($ columnRef , $ computedColumnNames )) {
307+ continue ; // Valid reference to another computed column
308+ }
309+
220310 // Check if it's a valid column reference
221311 if (!$ this ->isValidColumnReference ($ columnRef , $ table )) {
222312 $ errors [] = "Column reference ' {$ columnRef }' does not exist in table ' {$ tableName }' or its relationships " ;
0 commit comments