|
| 1 | +create or replace function update_page(page_id int, new_content text, |
| 2 | + editor_id int, new_comment text DEFAULT '', context_len int DEFAULT 3) returns int as $$ |
| 3 | +declare |
| 4 | + latest page_latest; |
| 5 | + new_revision int; |
| 6 | + hunk text[]; |
| 7 | + context text[]; -- only contains consecutive lines in LCS |
| 8 | + in_hunk boolean := FALSE; |
| 9 | + hunk_start int := 1; |
| 10 | + hunk_lines_added int := 0; |
| 11 | + hunk_lines_deleted int := 0; |
| 12 | + hunk_lines_context int := 0; |
| 13 | + ary1 text[]; |
| 14 | + ary2 text[]; |
| 15 | + LCS text[]; |
| 16 | + line1 text; |
| 17 | + line2 text; |
| 18 | + lineLCS text; |
| 19 | + ptr1 int := 1; |
| 20 | + ptr2 int := 1; |
| 21 | + ptrLCS int := 1; |
| 22 | +begin |
| 23 | + SELECT * INTO latest FROM page_latest WHERE id = page_id; |
| 24 | + IF NOT FOUND THEN |
| 25 | + RAISE EXCEPTION 'Page % not found', id; |
| 26 | + END IF; |
| 27 | + new_revision := latest.revision + 1; |
| 28 | + raise notice 'new revision: %', new_revision; |
| 29 | + -- write out new diff object |
| 30 | + INSERT INTO page_diff (page_id, revision, editor, comment) |
| 31 | + VALUES (page_id, latest.revision, latest.editor, latest.comment); |
| 32 | + -- make hunks |
| 33 | + ary1 := string_to_array(latest.content, E'\n'); |
| 34 | + ary2 := string_to_array(new_content, E'\n'); |
| 35 | + raise notice 'About to determine longest common substring'; |
| 36 | + LCS := lcs(ary1, ary2); |
| 37 | + raise notice 'Longest common substring determined'; |
| 38 | + line1 := ary1[ptr1]; |
| 39 | + line2 := ary2[ptr2]; |
| 40 | + lineLCS := LCS[ptrLCS]; |
| 41 | + LOOP |
| 42 | + if line1 is null and line2 is null and lineLCS is null then |
| 43 | + -- we're done! |
| 44 | + IF in_hunk THEN |
| 45 | + IF array_length(context, 1) IS NOT NULL THEN |
| 46 | + -- add context to hunk |
| 47 | + hunk := hunk || context; |
| 48 | + hunk_lines_context := hunk_lines_context + array_length(context, 1); |
| 49 | + END IF; |
| 50 | + -- write out the last hunk |
| 51 | + INSERT INTO page_diff_hunk (page_id, revision, start, |
| 52 | + content, lines_added, lines_deleted, lines_context) |
| 53 | + VALUES |
| 54 | + (page_id, latest.revision, hunk_start, array_to_string(hunk, E'\n'), |
| 55 | + hunk_lines_added, hunk_lines_deleted, hunk_lines_context); |
| 56 | + END IF; |
| 57 | + -- update the page_latest object |
| 58 | + UPDATE page_latest SET content = new_content, revision = new_revision, |
| 59 | + num_lines = array_length(ary2, 1), comment = new_comment, |
| 60 | + editor = editor_id, edited_on = now() |
| 61 | + WHERE id = page_id; |
| 62 | + return new_revision; |
| 63 | + end if; |
| 64 | + -- handle same line |
| 65 | + if line1 = lineLCS and line2 = lineLCS then |
| 66 | + raise notice 'equal lines: %', lineLCS; |
| 67 | + IF NOT in_hunk THEN |
| 68 | + -- LIFO queue |
| 69 | + IF array_length(context, 1) < context_len THEN |
| 70 | + context := context || (' ' || lineLCS); |
| 71 | + hunk_lines_context := hunk_lines_context + 1; |
| 72 | + ELSE |
| 73 | + context := context[2:context_len] || (' ' || lineLCS); |
| 74 | + END IF; |
| 75 | + ELSE |
| 76 | + context := context || (' ' || lineLCS); |
| 77 | + hunk_lines_context := hunk_lines_context + 1; |
| 78 | + -- are we done with this hunk? |
| 79 | + IF array_length(context, 1) = context_len THEN |
| 80 | + -- write out the hunk |
| 81 | + INSERT INTO page_diff_hunk (page_id, revision, start, |
| 82 | + content, lines_added, lines_deleted, lines_context) |
| 83 | + VALUES |
| 84 | + (page_id, latest.revision, hunk_start, array_to_string(hunk, E'\n'), |
| 85 | + hunk_lines_added, hunk_lines_deleted, hunk_lines_context); |
| 86 | + -- and reset |
| 87 | + hunk := array[]::text[]; |
| 88 | + context := array[]::text[]; |
| 89 | + in_hunk := FALSE; |
| 90 | + hunk_lines_added := 0; |
| 91 | + hunk_lines_deleted := 0; |
| 92 | + hunk_lines_context := 0; |
| 93 | + END IF; |
| 94 | + END IF; |
| 95 | + ptr1 := ptr1 + 1; |
| 96 | + ptr2 := ptr2 + 1; |
| 97 | + ptrLCS := ptrLCS + 1; |
| 98 | + line1 := ary1[ptr1]; |
| 99 | + line2 := ary2[ptr2]; |
| 100 | + lineLCS := LCS[ptrLCS]; |
| 101 | + continue; -- skip the rest of this function and go on |
| 102 | + end if; |
| 103 | + -- reset context array |
| 104 | + IF NOT in_hunk THEN |
| 105 | + -- start a new hunk |
| 106 | + hunk = context; |
| 107 | + in_hunk = TRUE; |
| 108 | + IF ptr1 > context_len THEN |
| 109 | + hunk_start = ptr1 - context_len; |
| 110 | + ELSE |
| 111 | + IF array_length(context, 1) IS NULL THEN |
| 112 | + hunk_start = ptr1; |
| 113 | + ELSE |
| 114 | + hunk_start = ptr1 - array_length(context, 1); |
| 115 | + END IF; |
| 116 | + END IF; |
| 117 | + ELSE |
| 118 | + IF array_length(context, 1) IS NOT NULL THEN |
| 119 | + -- add context to hunk |
| 120 | + hunk := hunk || context; |
| 121 | + hunk_lines_context := hunk_lines_context + array_length(context, 1); |
| 122 | + END IF; |
| 123 | + END IF; |
| 124 | + context := array[]::text[]; |
| 125 | + -- done resetting context; handle addition and deletion |
| 126 | + if line1 is not null and (line1 != lineLCS or lineLCS is null) then |
| 127 | + -- must have been deleted |
| 128 | + hunk := hunk || ('-' || line1); |
| 129 | + ptr1 := ptr1 + 1; |
| 130 | + line1 := ary1[ptr1]; |
| 131 | + hunk_lines_deleted := hunk_lines_deleted + 1; |
| 132 | + continue; |
| 133 | + end if; |
| 134 | + if line2 is not null and (line2 != lineLCS or lineLCS is null) then |
| 135 | + -- must have been added |
| 136 | + hunk := hunk || ('+' || line2); |
| 137 | + ptr2 := ptr2 + 1; |
| 138 | + line2 := ary2[ptr2]; |
| 139 | + hunk_lines_added := hunk_lines_added + 1; |
| 140 | + continue; |
| 141 | + end if; |
| 142 | + END LOOP; |
| 143 | +end; |
| 144 | +$$ language plpgsql |
| 145 | +VOLATILE STRICT; |
| 146 | + |
0 commit comments