@@ -25,31 +25,13 @@ import {
2525 type AgGridFilterModel ,
2626 type AgGridFilter ,
2727} from '@superset-ui/core' ;
28- import { getStartOfDay , getEndOfDay } from './utils/agGridFilterConverter' ;
29-
30- /**
31- * AG Grid text filter type to backend operator mapping
32- */
33- const TEXT_FILTER_OPERATORS : Record < string , string > = {
34- equals : '=' ,
35- notEqual : '!=' ,
36- contains : 'ILIKE' ,
37- notContains : 'NOT ILIKE' ,
38- startsWith : 'ILIKE' ,
39- endsWith : 'ILIKE' ,
40- } ;
41-
42- /**
43- * AG Grid number filter type to backend operator mapping
44- */
45- const NUMBER_FILTER_OPERATORS : Record < string , string > = {
46- equals : '=' ,
47- notEqual : '!=' ,
48- lessThan : '<' ,
49- lessThanOrEqual : '<=' ,
50- greaterThan : '>' ,
51- greaterThanOrEqual : '>=' ,
52- } ;
28+ import {
29+ getStartOfDay ,
30+ getEndOfDay ,
31+ FILTER_OPERATORS ,
32+ SQL_OPERATORS ,
33+ validateColumnName ,
34+ } from './utils/agGridFilterConverter' ;
5335
5436/**
5537 * Maps custom server-side date filter operators to normalized operator names.
@@ -58,29 +40,29 @@ const NUMBER_FILTER_OPERATORS: Record<string, string> = {
5840 */
5941const DATE_FILTER_OPERATOR_MAP : Record < string , string > = {
6042 // Standard operators
61- equals : 'equals' ,
62- notEqual : 'notEqual' ,
63- lessThan : 'lessThan' ,
64- lessThanOrEqual : 'lessThanOrEqual' ,
65- greaterThan : 'greaterThan' ,
66- greaterThanOrEqual : 'greaterThanOrEqual' ,
67- inRange : 'inRange' ,
43+ [ FILTER_OPERATORS . EQUALS ] : FILTER_OPERATORS . EQUALS ,
44+ [ FILTER_OPERATORS . NOT_EQUAL ] : FILTER_OPERATORS . NOT_EQUAL ,
45+ [ FILTER_OPERATORS . LESS_THAN ] : FILTER_OPERATORS . LESS_THAN ,
46+ [ FILTER_OPERATORS . LESS_THAN_OR_EQUAL ] : FILTER_OPERATORS . LESS_THAN_OR_EQUAL ,
47+ [ FILTER_OPERATORS . GREATER_THAN ] : FILTER_OPERATORS . GREATER_THAN ,
48+ [ FILTER_OPERATORS . GREATER_THAN_OR_EQUAL ] : FILTER_OPERATORS . GREATER_THAN_OR_EQUAL ,
49+ [ FILTER_OPERATORS . IN_RANGE ] : FILTER_OPERATORS . IN_RANGE ,
6850 // Custom server-side operators (map to standard equivalents)
69- serverEquals : 'equals' ,
70- serverNotEqual : 'notEqual' ,
71- serverBefore : 'lessThan' ,
72- serverAfter : 'greaterThan' ,
73- serverInRange : 'inRange' ,
51+ [ FILTER_OPERATORS . SERVER_EQUALS ] : FILTER_OPERATORS . EQUALS ,
52+ [ FILTER_OPERATORS . SERVER_NOT_EQUAL ] : FILTER_OPERATORS . NOT_EQUAL ,
53+ [ FILTER_OPERATORS . SERVER_BEFORE ] : FILTER_OPERATORS . LESS_THAN ,
54+ [ FILTER_OPERATORS . SERVER_AFTER ] : FILTER_OPERATORS . GREATER_THAN ,
55+ [ FILTER_OPERATORS . SERVER_IN_RANGE ] : FILTER_OPERATORS . IN_RANGE ,
7456} ;
7557
7658/**
7759 * Blank filter operator types
7860 */
7961const BLANK_OPERATORS = new Set ( [
80- 'blank' ,
81- 'notBlank' ,
82- 'serverBlank' ,
83- 'serverNotBlank' ,
62+ FILTER_OPERATORS . BLANK ,
63+ FILTER_OPERATORS . NOT_BLANK ,
64+ FILTER_OPERATORS . SERVER_BLANK ,
65+ FILTER_OPERATORS . SERVER_NOT_BLANK ,
8466] ) ;
8567
8668/** Escapes single quotes in SQL strings: O'Hara → O''Hara */
@@ -89,13 +71,13 @@ function escapeStringValue(value: string): string {
8971}
9072
9173function getTextComparator ( type : string , value : string ) : string {
92- if ( type === 'contains' || type === 'notContains' ) {
74+ if ( type === FILTER_OPERATORS . CONTAINS || type === FILTER_OPERATORS . NOT_CONTAINS ) {
9375 return `%${ value } %` ;
9476 }
95- if ( type === 'startsWith' ) {
77+ if ( type === FILTER_OPERATORS . STARTS_WITH ) {
9678 return `${ value } %` ;
9779 }
98- if ( type === 'endsWith' ) {
80+ if ( type === FILTER_OPERATORS . ENDS_WITH ) {
9981 return `%${ value } ` ;
10082 }
10183 return value ;
@@ -122,33 +104,33 @@ function convertDateFilterToSQL(
122104 const normalizedType = DATE_FILTER_OPERATOR_MAP [ type ] || type ;
123105
124106 switch ( normalizedType ) {
125- case 'equals' :
107+ case FILTER_OPERATORS . EQUALS :
126108 if ( ! dateFrom ) return null ;
127109 // Full day range for equals
128110 return `(${ colId } >= '${ getStartOfDay ( dateFrom ) } ' AND ${ colId } <= '${ getEndOfDay ( dateFrom ) } ')` ;
129111
130- case 'notEqual' :
112+ case FILTER_OPERATORS . NOT_EQUAL :
131113 if ( ! dateFrom ) return null ;
132114 // Outside the full day range for not equals
133115 return `(${ colId } < '${ getStartOfDay ( dateFrom ) } ' OR ${ colId } > '${ getEndOfDay ( dateFrom ) } ')` ;
134116
135- case 'lessThan' :
117+ case FILTER_OPERATORS . LESS_THAN :
136118 if ( ! dateFrom ) return null ;
137119 return `${ colId } < '${ getStartOfDay ( dateFrom ) } '` ;
138120
139- case 'lessThanOrEqual' :
121+ case FILTER_OPERATORS . LESS_THAN_OR_EQUAL :
140122 if ( ! dateFrom ) return null ;
141123 return `${ colId } <= '${ getEndOfDay ( dateFrom ) } '` ;
142124
143- case 'greaterThan' :
125+ case FILTER_OPERATORS . GREATER_THAN :
144126 if ( ! dateFrom ) return null ;
145127 return `${ colId } > '${ getEndOfDay ( dateFrom ) } '` ;
146128
147- case 'greaterThanOrEqual' :
129+ case FILTER_OPERATORS . GREATER_THAN_OR_EQUAL :
148130 if ( ! dateFrom ) return null ;
149131 return `${ colId } >= '${ getStartOfDay ( dateFrom ) } '` ;
150132
151- case 'inRange' :
133+ case FILTER_OPERATORS . IN_RANGE :
152134 if ( ! dateFrom || ! dateTo ) return null ;
153135 return `${ colId } BETWEEN '${ getStartOfDay ( dateFrom ) } ' AND '${ getEndOfDay ( dateTo ) } '` ;
154136
@@ -207,6 +189,11 @@ function convertFilterToSQL(
207189 colId : string ,
208190 filter : AgGridFilter ,
209191) : string | null {
192+ // Validate column name to prevent SQL injection and malformed queries
193+ if ( ! validateColumnName ( colId ) ) {
194+ return null ;
195+ }
196+
210197 // Complex filter: has operator and conditions
211198 if (
212199 filter . operator &&
@@ -231,31 +218,64 @@ function convertFilterToSQL(
231218 // Handle blank/notBlank operators for all filter types
232219 // These are special operators that check for NULL values
233220 if ( filter . type && BLANK_OPERATORS . has ( filter . type ) ) {
234- if ( filter . type === 'blank' || filter . type === 'serverBlank' ) {
235- return `${ colId } IS NULL` ;
221+ if (
222+ filter . type === FILTER_OPERATORS . BLANK ||
223+ filter . type === FILTER_OPERATORS . SERVER_BLANK
224+ ) {
225+ return `${ colId } ${ SQL_OPERATORS . IS_NULL } ` ;
236226 }
237- if ( filter . type === 'notBlank' || filter . type === 'serverNotBlank' ) {
238- return `${ colId } IS NOT NULL` ;
227+ if (
228+ filter . type === FILTER_OPERATORS . NOT_BLANK ||
229+ filter . type === FILTER_OPERATORS . SERVER_NOT_BLANK
230+ ) {
231+ return `${ colId } ${ SQL_OPERATORS . IS_NOT_NULL } ` ;
239232 }
240233 }
241234
242235 if ( filter . filterType === 'text' && filter . filter && filter . type ) {
243- const op = TEXT_FILTER_OPERATORS [ filter . type ] ;
244236 const escapedFilter = escapeStringValue ( String ( filter . filter ) ) ;
245237 const val = getTextComparator ( filter . type , escapedFilter ) ;
246238
247- return op === 'ILIKE' || op === 'NOT ILIKE'
248- ? `${ colId } ${ op } '${ val } '`
249- : `${ colId } ${ op } '${ escapedFilter } '` ;
239+ // Map text filter types to SQL operators
240+ switch ( filter . type ) {
241+ case FILTER_OPERATORS . EQUALS :
242+ return `${ colId } ${ SQL_OPERATORS . EQUALS } '${ escapedFilter } '` ;
243+ case FILTER_OPERATORS . NOT_EQUAL :
244+ return `${ colId } ${ SQL_OPERATORS . NOT_EQUALS } '${ escapedFilter } '` ;
245+ case FILTER_OPERATORS . CONTAINS :
246+ return `${ colId } ${ SQL_OPERATORS . ILIKE } '${ val } '` ;
247+ case FILTER_OPERATORS . NOT_CONTAINS :
248+ return `${ colId } ${ SQL_OPERATORS . NOT_ILIKE } '${ val } '` ;
249+ case FILTER_OPERATORS . STARTS_WITH :
250+ case FILTER_OPERATORS . ENDS_WITH :
251+ return `${ colId } ${ SQL_OPERATORS . ILIKE } '${ val } '` ;
252+ default :
253+ return null ;
254+ }
250255 }
251256
252257 if (
253258 filter . filterType === 'number' &&
254259 filter . filter !== undefined &&
255260 filter . type
256261 ) {
257- const op = NUMBER_FILTER_OPERATORS [ filter . type ] ;
258- return `${ colId } ${ op } ${ filter . filter } ` ;
262+ // Map number filter types to SQL operators
263+ switch ( filter . type ) {
264+ case FILTER_OPERATORS . EQUALS :
265+ return `${ colId } ${ SQL_OPERATORS . EQUALS } ${ filter . filter } ` ;
266+ case FILTER_OPERATORS . NOT_EQUAL :
267+ return `${ colId } ${ SQL_OPERATORS . NOT_EQUALS } ${ filter . filter } ` ;
268+ case FILTER_OPERATORS . LESS_THAN :
269+ return `${ colId } ${ SQL_OPERATORS . LESS_THAN } ${ filter . filter } ` ;
270+ case FILTER_OPERATORS . LESS_THAN_OR_EQUAL :
271+ return `${ colId } ${ SQL_OPERATORS . LESS_THAN_OR_EQUAL } ${ filter . filter } ` ;
272+ case FILTER_OPERATORS . GREATER_THAN :
273+ return `${ colId } ${ SQL_OPERATORS . GREATER_THAN } ${ filter . filter } ` ;
274+ case FILTER_OPERATORS . GREATER_THAN_OR_EQUAL :
275+ return `${ colId } ${ SQL_OPERATORS . GREATER_THAN_OR_EQUAL } ${ filter . filter } ` ;
276+ default :
277+ return null ;
278+ }
259279 }
260280
261281 // Handle date filters with proper date formatting and custom server operators
0 commit comments