Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | 13x 244x 14x 25x 65x 18x 106x 10x 3x 3x 86x 38x 2x 4x 2x 27x 6x 3x 3x 1x 1x 1x 145x 38x 38x 38x 8x 33110x 276x 44378x | /**
* PostgreSQL Database Adapter
* Implements PostgreSQL-specific SQL generation for time dimensions, string matching, and type casting
* Extracted from hardcoded logic in executor.ts and multi-cube-builder.ts
*/
import { sql, type SQL, type AnyColumn } from 'drizzle-orm'
import type { TimeGranularity } from '../types'
import { BaseDatabaseAdapter } from './base-adapter'
export class PostgresAdapter extends BaseDatabaseAdapter {
getEngineType(): 'postgres' {
return 'postgres'
}
/**
* Build PostgreSQL time dimension using DATE_TRUNC function
* Extracted from executor.ts:649-670 and multi-cube-builder.ts:306-320
*/
buildTimeDimension(granularity: TimeGranularity, fieldExpr: AnyColumn | SQL): SQL {
// PostgreSQL uses DATE_TRUNC with explicit timestamp casting
switch (granularity) {
case 'year':
return sql`DATE_TRUNC('year', ${fieldExpr}::timestamp)`
case 'quarter':
return sql`DATE_TRUNC('quarter', ${fieldExpr}::timestamp)`
case 'month':
return sql`DATE_TRUNC('month', ${fieldExpr}::timestamp)`
case 'week':
return sql`DATE_TRUNC('week', ${fieldExpr}::timestamp)`
case 'day':
// Ensure we return the truncated date as a timestamp
return sql`DATE_TRUNC('day', ${fieldExpr}::timestamp)::timestamp`
case 'hour':
return sql`DATE_TRUNC('hour', ${fieldExpr}::timestamp)`
case 'minute':
return sql`DATE_TRUNC('minute', ${fieldExpr}::timestamp)`
case 'second':
return sql`DATE_TRUNC('second', ${fieldExpr}::timestamp)`
default:
// Fallback to the original expression if granularity is not recognized
return fieldExpr as SQL
}
}
/**
* Build PostgreSQL string matching conditions using ILIKE (case-insensitive)
* Extracted from executor.ts:807-813 and multi-cube-builder.ts:468-474
*/
buildStringCondition(fieldExpr: AnyColumn | SQL, operator: 'contains' | 'notContains' | 'startsWith' | 'endsWith' | 'like' | 'notLike' | 'ilike' | 'regex' | 'notRegex', value: string): SQL {
switch (operator) {
case 'contains':
return sql`${fieldExpr} ILIKE ${`%${value}%`}`
case 'notContains':
return sql`${fieldExpr} NOT ILIKE ${`%${value}%`}`
case 'startsWith':
return sql`${fieldExpr} ILIKE ${`${value}%`}`
case 'endsWith':
return sql`${fieldExpr} ILIKE ${`%${value}`}`
case 'like':
return sql`${fieldExpr} LIKE ${value}`
case 'notLike':
return sql`${fieldExpr} NOT LIKE ${value}`
case 'ilike':
return sql`${fieldExpr} ILIKE ${value}`
case 'regex':
return sql`${fieldExpr} ~* ${value}`
case 'notRegex':
return sql`${fieldExpr} !~* ${value}`
default:
throw new Error(`Unsupported string operator: ${operator}`)
}
}
/**
* Build PostgreSQL type casting using :: syntax
* Extracted from various locations where ::timestamp was used
*/
castToType(fieldExpr: AnyColumn | SQL, targetType: 'timestamp' | 'decimal' | 'integer'): SQL {
switch (targetType) {
case 'timestamp':
return sql`${fieldExpr}::timestamp`
case 'decimal':
return sql`${fieldExpr}::decimal`
case 'integer':
return sql`${fieldExpr}::integer`
default:
throw new Error(`Unsupported cast type: ${targetType}`)
}
}
/**
* Build PostgreSQL AVG aggregation with COALESCE for NULL handling
* PostgreSQL AVG returns NULL for empty sets, so we use COALESCE for consistent behavior
* Extracted from multi-cube-builder.ts:284
*/
buildAvg(fieldExpr: AnyColumn | SQL): SQL {
return sql`COALESCE(AVG(${fieldExpr}), 0)`
}
/**
* Build PostgreSQL CASE WHEN conditional expression
*/
buildCaseWhen(conditions: Array<{ when: SQL; then: any }>, elseValue?: any): SQL {
const cases = conditions.map(c => sql`WHEN ${c.when} THEN ${c.then}`).reduce((acc, curr) => sql`${acc} ${curr}`)
Iif (elseValue !== undefined) {
return sql`CASE ${cases} ELSE ${elseValue} END`
}
return sql`CASE ${cases} END`
}
/**
* Build PostgreSQL boolean literal
* PostgreSQL uses TRUE/FALSE keywords
*/
buildBooleanLiteral(value: boolean): SQL {
return value ? sql`TRUE` : sql`FALSE`
}
/**
* Convert filter values - PostgreSQL uses native types
* No conversion needed for PostgreSQL
*/
convertFilterValue(value: any): any {
return value
}
/**
* Prepare date value for PostgreSQL
* PostgreSQL accepts Date objects directly
*/
prepareDateValue(date: Date): any {
return date
}
/**
* PostgreSQL stores timestamps as native timestamp types
*/
isTimestampInteger(): boolean {
return false
}
/**
* PostgreSQL time dimensions already return proper values
* No conversion needed
*/
convertTimeDimensionResult(value: any): any {
return value
}
} |