bookwiz.io / supabase / migrations / 023_fix_time_casting_issue.sql
023_fix_time_casting_issue.sql
Raw
-- Fix time casting issue in get_current_usage_period function
-- Error: cannot cast type time with time zone to timestamp with time zone
-- This happens when current_period_end contains TIME data instead of TIMESTAMP data

-- Create a robust version that handles different timestamp formats
CREATE OR REPLACE FUNCTION get_current_usage_period(user_uuid UUID)
RETURNS TABLE (
    period_start TIMESTAMPTZ,
    period_end TIMESTAMPTZ
)
SECURITY DEFINER
SET search_path = public, pg_catalog, pg_temp
AS $$
DECLARE
    subscription_record RECORD;
    current_time TIMESTAMPTZ := NOW();
    safe_period_end TIMESTAMPTZ;
BEGIN
    -- Get the user's active subscription with safe timestamp handling
    SELECT 
        id,
        user_id, 
        status,
        current_period_start,
        current_period_end,
        created_at
    INTO subscription_record
    FROM subscriptions s
    WHERE s.user_id = user_uuid 
    AND s.status IN ('active', 'trialing', 'past_due')
    ORDER BY s.created_at DESC
    LIMIT 1;
    
    -- Check if we have a subscription and handle the timestamp conversion safely
    IF subscription_record IS NOT NULL AND subscription_record.current_period_end IS NOT NULL THEN
        -- Try to safely convert current_period_end to TIMESTAMPTZ
        BEGIN
            -- If it's already a TIMESTAMPTZ, this will work directly
            safe_period_end := subscription_record.current_period_end;
            
            -- Check if this subscription is still active
            IF safe_period_end > current_time THEN
                -- Return subscription period
                RETURN QUERY 
                SELECT 
                    subscription_record.current_period_start,
                    safe_period_end;
                RETURN;
            END IF;
        EXCEPTION 
            WHEN OTHERS THEN
                -- If timestamp conversion fails, log and fall through to free tier
                RAISE LOG 'Failed to convert subscription period_end for user %: %', user_uuid, SQLERRM;
        END;
    END IF;
    
    -- Return current month for free tier (or when subscription processing fails)
    RETURN QUERY
    SELECT 
        DATE_TRUNC('month', current_time)::TIMESTAMPTZ,
        (DATE_TRUNC('month', current_time) + INTERVAL '1 month')::TIMESTAMPTZ;
END;
$$ LANGUAGE plpgsql;

-- Also update the user_has_active_subscription function to be more robust
CREATE OR REPLACE FUNCTION user_has_active_subscription(user_uuid UUID)
RETURNS BOOLEAN
SECURITY DEFINER
SET search_path = public, pg_catalog, pg_temp
AS $$
DECLARE
    subscription_count INTEGER;
    current_time TIMESTAMPTZ := NOW();
BEGIN
    -- Use a safer approach that doesn't rely on casting
    SELECT COUNT(*) INTO subscription_count
    FROM subscriptions 
    WHERE user_id = user_uuid 
    AND status IN ('active', 'trialing')
    AND (
        current_period_end IS NULL 
        OR (
            current_period_end IS NOT NULL 
            AND current_period_end > current_time
        )
    );
    
    RETURN subscription_count > 0;
EXCEPTION 
    WHEN OTHERS THEN
        -- If there's any error, assume no active subscription
        RAISE LOG 'Error checking subscription for user %: %', user_uuid, SQLERRM;
        RETURN FALSE;
END;
$$ LANGUAGE plpgsql;

-- Add helpful comments
COMMENT ON FUNCTION get_current_usage_period(UUID) IS 'Fixed time casting issue with robust timestamp handling and error recovery';
COMMENT ON FUNCTION user_has_active_subscription(UUID) IS 'Fixed time casting issue with safer comparison logic';