Skip to content

Conversation

@juleswritescode
Copy link
Collaborator

@juleswritescode juleswritescode commented Aug 30, 2025

Screenshot 2025-08-30 at 11 06 35

@juleswritescode juleswritescode changed the base branch from main to feat/hover-col August 30, 2025 09:02
@reteps
Copy link

reteps commented Aug 30, 2025

We have a couple of large functions -- e.g. 233 line sproc. For these functions, is there a possibility of an option to just show the signature, and not the function body?

e.g.

    sync_course_instances(
        IN disk_course_instances_data jsonb[],
        IN syncing_course_id bigint,
        OUT name_to_id_map jsonb
    )

and not:

full body
CREATE FUNCTION
    sync_course_instances(
        IN disk_course_instances_data jsonb[],
        IN syncing_course_id bigint,
        OUT name_to_id_map jsonb
    )
AS $$
DECLARE
    missing_dest_short_names TEXT;
    missing_src_short_names TEXT;
    mismatched_uuid_short_names TEXT;
    valid_course_instance record;
    syncing_course_instance_id bigint;
    course_instance_timezone TEXT;
    enrollment JSONB;
BEGIN
    -- The sync algorithm used here is described in the preprint
    -- "Preserving identity during opportunistic unidirectional
    -- synchronization via two-fold identifiers".

    -- Move all our data into a temporary table so it's easier to work with

    CREATE TEMPORARY TABLE disk_course_instances (
        short_name TEXT NOT NULL,
        uuid uuid,
        enrollment_code VARCHAR(255),
        errors TEXT,
        warnings TEXT,
        data JSONB
    ) ON COMMIT DROP;

    INSERT INTO disk_course_instances (
        short_name,
        uuid,
        enrollment_code,
        errors,
        warnings,
        data
    ) SELECT
        entries->>0,
        (entries->>1)::uuid,
        entries->>2,
        entries->>3,
        entries->>4,
        (entries->5)::JSONB
    FROM UNNEST(disk_course_instances_data) AS entries;

    -- Synchronize the dest (course_instances) with the src
    -- (disk_course_instances). This soft-deletes, un-soft-deletes,
    -- and inserts new rows in course_instances. No data is synced
    -- yet. Only the (id, course_id, uuid, short_name, deleted_at)
    -- columns are used.

    WITH
    matched_rows AS (
        -- See `sync_questions.sql` for an explanation of the use of DISTINCT ON.
        SELECT DISTINCT ON (src_short_name)
            src.short_name AS src_short_name,
            src.uuid AS src_uuid,
            -- This enrollment code is only used for inserts, and not used on updates
            src.enrollment_code AS src_enrollment_code,
            dest.id AS dest_id
        FROM disk_course_instances AS src LEFT JOIN course_instances AS dest ON (
            dest.course_id = syncing_course_id
            AND (
                src.uuid = dest.uuid
                OR (
                    (src.uuid IS NULL OR dest.uuid IS NULL)
                    AND src.short_name = dest.short_name AND dest.deleted_at IS NULL
                )
            )
        )
        ORDER BY src_short_name, (src.uuid = dest.uuid) DESC NULLS LAST
    ),
    deactivate_unmatched_dest_rows AS (
        UPDATE course_instances AS dest
        SET deleted_at = now()
        WHERE dest.id NOT IN (
            SELECT dest_id FROM matched_rows WHERE dest_id IS NOT NULL
        ) AND dest.deleted_at IS NULL AND dest.course_id = syncing_course_id
    ),
    update_matched_dest_rows AS (
        UPDATE course_instances AS dest
        SET short_name = src_short_name, uuid = src_uuid, deleted_at = NULL
        FROM matched_rows
        WHERE dest.id = dest_id AND dest.course_id = syncing_course_id
    ),
    insert_unmatched_src_rows AS (
        -- UTC is used as a temporary timezone, which will be updated in following statements
        INSERT INTO course_instances AS dest
            (course_id, short_name, uuid, display_timezone, deleted_at, enrollment_code)
        SELECT syncing_course_id, src_short_name, src_uuid, 'UTC', NULL, src_enrollment_code
        FROM matched_rows
        WHERE dest_id IS NULL
        -- This is a total hack, but the test suite hardcoded course instance ID 1
        -- for the test course in a lot of places. To avoid having to change tons
        -- of tests after adding a new course instance to the test course, we'll
        -- alphabetically sort the course instances by name to ensure that `Sp15`
        -- will have ID 1. This assumes that it is in fact that first course instance,
        -- which is currently true.
        --
        -- Mainly, this ensures that the tests are deterministic. So even if we do
        -- add a new course instance, the tests will fail if a new course instance
        -- would be assigned ID 1.
        --
        -- We specifically use C collation to ensure that "Sp15" ends up before "public".
        ORDER BY src_short_name COLLATE "C" ASC
        RETURNING dest.short_name AS src_short_name, dest.id AS inserted_dest_id
    )
    -- Make a map from CIID to ID to return to the caller
    SELECT COALESCE(jsonb_object_agg(src_short_name, COALESCE(dest_id, inserted_dest_id)), '{}'::JSONB)
    INTO name_to_id_map
    FROM matched_rows LEFT JOIN insert_unmatched_src_rows USING (src_short_name);

    -- Internal consistency checks to ensure that dest (course_instances) and
    -- src (disk_course_instances) are in fact synchronized.

    SELECT string_agg(src.short_name, ', ')
    INTO missing_dest_short_names
    FROM disk_course_instances AS src
    WHERE src.short_name NOT IN (SELECT dest.short_name FROM course_instances AS dest WHERE dest.course_id = syncing_course_id AND dest.deleted_at IS NULL);
    IF (missing_dest_short_names IS NOT NULL) THEN
        RAISE EXCEPTION 'Assertion failure: CIIDs on disk but not synced to DB: %', missing_dest_short_names;
    END IF;

    SELECT string_agg(dest.short_name, ', ')
    INTO missing_src_short_names
    FROM course_instances AS dest
    WHERE dest.course_id = syncing_course_id AND dest.deleted_at IS NULL AND dest.short_name NOT IN (SELECT src.short_name FROM disk_course_instances AS src);
    IF (missing_src_short_names IS NOT NULL) THEN
        RAISE EXCEPTION 'Assertion failure: CIIDs in DB but not on disk: %', missing_src_short_names;
    END IF;

    SELECT string_agg(src.short_name, ', ')
    INTO mismatched_uuid_short_names
    FROM disk_course_instances AS src JOIN course_instances AS dest ON (dest.course_id = syncing_course_id AND dest.short_name = src.short_name AND dest.deleted_at IS NULL)
    WHERE NOT (src.uuid = dest.uuid OR src.uuid IS NULL);
    IF (mismatched_uuid_short_names IS NOT NULL) THEN
        RAISE EXCEPTION 'Assertion failure: CIIDs on disk with mismatched UUIDs in DB: %', mismatched_uuid_short_names;
    END IF;

    -- At this point, there will be exactly one non-deleted row for
    -- all short_names that we loaded from disk. It is now safe to
    -- update all those rows with the new information from disk (if we
    -- have any).

    -- First pass: update complete information for all course instances without errors.
    UPDATE course_instances AS dest
    SET
        long_name = src.data->>'long_name',
        assessments_group_by = (src.data->>'assessments_group_by')::enum_assessment_grouping,
        display_timezone = COALESCE(src.data->>'display_timezone', c.display_timezone),
        hide_in_enroll_page = (src.data->>'hide_in_enroll_page')::boolean,
        json_comment = (src.data->>'comment')::jsonb,
        share_source_publicly = (src.data->>'share_source_publicly')::boolean,
        sync_errors = NULL,
        sync_warnings = src.warnings
    FROM
        disk_course_instances AS src
        JOIN pl_courses AS c ON (c.id = syncing_course_id)
    WHERE
        dest.short_name = src.short_name
        AND dest.deleted_at IS NULL
        AND dest.course_id = syncing_course_id
        AND (src.errors IS NULL OR src.errors = '');

    -- Now, sync access rules for all valid course instances.
    WITH valid_course_instances AS (
        SELECT short_name, data
        FROM disk_course_instances AS src
        WHERE (src.errors IS NULL OR src.errors = '')
    ), synced_course_instances AS (
        SELECT
            ci.id,
            ci.display_timezone,
            vci.data
        FROM course_instances AS ci
        JOIN valid_course_instances AS vci ON (vci.short_name = ci.short_name)
        WHERE
            ci.course_id = syncing_course_id
            AND ci.deleted_at IS NULL
    ), inserted_access_rules AS (
        INSERT INTO course_instance_access_rules (
            course_instance_id,
            number,
            uids,
            start_date,
            end_date,
            institution,
            json_comment
        )
        SELECT
            ci.id,
            number,
            CASE
                WHEN access_rule->'uids' = null::JSONB THEN NULL
                ELSE jsonb_array_to_text_array(access_rule->'uids')
            END,
            input_date(access_rule->>'start_date', ci.display_timezone),
            input_date(access_rule->>'end_date', ci.display_timezone),
            access_rule->>'institution',
            access_rule->'comment'
        FROM
            synced_course_instances AS ci,
            JSONB_ARRAY_ELEMENTS(ci.data->'access_rules') WITH ORDINALITY AS t(access_rule, number)
        ON CONFLICT (number, course_instance_id) DO UPDATE
        SET
            uids = EXCLUDED.uids,
            start_date = EXCLUDED.start_date,
            end_date = EXCLUDED.end_date,
            institution = EXCLUDED.institution,
            json_comment = EXCLUDED.json_comment
    )
    DELETE FROM course_instance_access_rules AS ciar
    USING
        synced_course_instances AS ci
    WHERE
        ciar.course_instance_id = ci.id
        AND ciar.number > JSONB_ARRAY_LENGTH(ci.data->'access_rules');

    -- Second pass: add errors where needed.
    UPDATE course_instances AS dest
    SET
        sync_errors = src.errors,
        sync_warnings = src.warnings
    FROM disk_course_instances AS src
    WHERE
        dest.short_name = src.short_name
        AND dest.deleted_at IS NULL
        AND dest.course_id = syncing_course_id
        AND (src.errors IS NOT NULL AND src.errors != '');
END;
$$ LANGUAGE plpgsql VOLATILE;

@juleswritescode
Copy link
Collaborator Author

We have a couple of large functions -- e.g. 233 line sproc. For these functions, is there a possibility of an option to just show the signature, and not the function body?

Very good point! Done.

# Input
```sql
select count(*) from users
↑ hovered here
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahh thats so beautiful

@juleswritescode juleswritescode merged commit 3dda257 into feat/hover-col Sep 1, 2025
8 checks passed
@juleswritescode juleswritescode deleted the feat/hover-fn branch October 28, 2025 15:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants