* Builds a typed range comparison against a JSONB cell. * * `number` columns cast both sides to `numeric`; `date` columns cast both sides * to `timestamptz` so date strings compare chronologically and timezone offsets * in ISO strings (e.g. `2024-01-01T00:00:00Z`) are preserved rather than * si
( tableName: string, field: string, operator: '>' | '>=' | '<' | '<=', value: number | string, columnType: ColumnType | undefined )
| 475 | * rows (bounded by the btree prefix on `table_id`). |
| 476 | */ |
| 477 | function buildComparisonClause( |
| 478 | tableName: string, |
| 479 | field: string, |
| 480 | operator: '>' | '>=' | '<' | '<=', |
| 481 | value: number | string, |
| 482 | columnType: ColumnType | undefined |
| 483 | ): SQL { |
| 484 | const escapedField = field.replace(/'/g, "''") |
| 485 | const cast = jsonbCastForType(columnType) ?? 'numeric' |
| 486 | validateComparisonValue(field, columnType, cast, value) |
| 487 | const cell = sql.raw(`(${tableName}.data->>'${escapedField}')::${cast}`) |
| 488 | return cast === 'timestamptz' |
| 489 | ? sql`${cell} ${sql.raw(operator)} ${value}::timestamptz` |
| 490 | : sql`${cell} ${sql.raw(operator)} ${value}` |
| 491 | } |
| 492 | |
| 493 | /** Escapes LIKE/ILIKE wildcard characters so they match literally */ |
| 494 | export function escapeLikePattern(value: string): string { |
no test coverage detected