On Wed, Jul 11, 2018 at 03:13:30PM -0400, Alvaro Herrera wrote:
> On 2018-Jun-06, Nico Williams wrote:
> > I've finally gotten around to rebasing this patch and making the change
> > that was requested, which was: merge the now-would-be-three deferral-
> > related bool columns in various pg_catalog tables into one char column.
> > 
> > Instead of (deferrable, initdeferred, alwaysdeferred), now there is just
> > (deferral).
> 
> Nice stuff.
> 
> Please add #defines for the chars in pg_constraint.h instead of using
> the values directly in the source.  Also, catalogs.sgml has a typo in
> one of the values.
> 
> What happens if you do SET CONSTRAINTS ALL IMMEDIATE and you have one of
> these constraints?  I expect that it silently does not alter that
> constraint, but you didn't change that manpage.

Correct, that's the idea, that it should be possible to write deferred
constraints/triggers which users cannot make immediate.  For example, an
audit extension that logs changes via FOR EACH ROW ALWAYS DEFERRED, or
my PoC COMMIT TRIGGER implementation (which depends on deferred
triggers).

I missed that there is a page for SET CONSTRAINTS!  I'll update it.

> I suggest not to include psql/tab-complete changes with your main patch.
> If you want to update it, split it out to its own patch.  Committer can
> choose to include it in one commit or not (I'm mildly inclined not to,
> but I'm probably inconsistent about it), but for review IMO it's better
> not to mix things -- It's way too easy to get entangled in silly details
> while editing that code, and it's not critical anyway.

OK, sure, though, of course, the committer could always leave that out
themselves, no?

To me though it seems that the change should be complete.

> > Incidentally, I had to do commit-by-commit rebasing to make the rebase
> > easier.  I have a shell function I use for this, if anyone wants a copy
> > of it -- sometimes it's much easier to do this than to do one huge jump.
> 
> I've done this manually a few times.  Please share, I'm curious.

OK, attached is my latest version of that script, though this one is a
bit changed from the one I used.  This version tries to be faster / more
efficient by first doing 1 commit, then 2, then 3, and so on, and on
conflict aborts and halves N to try again.  The idea is to only have to
merge conflicts at each commit where conflicts arise, never resolving
conflicts across more than one commit -- this makes is much easier to
reason about conflicts!

Note that the script is actually a shell function, and that it keeps
state in shel variables.  A better implementation would do the sort of
thing that git(1) itself does to keep rebase state.

Nico
-- 
# Based on a shell function by Viktor Dukhovni
#
# slowrebase BRANCH_TO_REBASE ONTO
function slowrebase {
    typeset b N

    if (($# > 0)) && [[ $1 = -h || $1 = --help ]]; then
        printf 'Usage: slowrebase BRANCH_TO_REBASE ONTO_HEAD\n'
        printf '       slowrebase # to continue after resolving conflicts\n'
        printf '\n\tslowrebase is a shell function that uses the following\n'
        printf '\tglobal variables to keep state: $S $T $B ${C[@]}\n'
        printf '\t                                $slowrebase_window_sz\n'
        printf '\tDO NOT CHANGE THOSE VARIABLES.\n'
        return 0
    elif (($# > 0 && $# != 2)); then
        printf 'Usage: slowrebase BRANCH_TO_REBASE ONTO_HEAD\n' 1>&2
        printf '       slowrebase # to continue after resolving conflicts\n'
        printf '\n\tslowrebase is a shell function that uses the following\n' 
1>&2
        printf '\tglobal variables to keep state: $S $T $B ${C[@]}\n' 1>&2
        printf '\t                                $slowrebase_window_sz\n' 1>&2
        printf '\tDO NOT CHANGE THOSE VARIABLES.\n' 1>&2
        return 1
    fi

    if (($# == 2)); then
        slowrebase_window_sz=1
        S=$1
        T=$2
        B=$(git merge-base "$S" "$T")
        C=( $(git log --oneline "$B".."$2" | awk '{print $1}') )
        set --

        # Prep
        git log -n1 "$S" > /dev/null || return 1
        if [[ $(git log --oneline -n1 HEAD) != $(git log --oneline -n1 "$S") 
]]; then
            if (($(git status -sb | wc -l) != 1)); then
                printf 'Error: please clean your workspace\n'
                return 1
            fi
            git checkout "$S"
        elif (($(git status -sbuno | wc -l) != 1)); then
            printf 'Error: please clean your workspace\n'
            return 1
        fi

        # Fall through to get started
    elif [[ $(git log --oneline -n1 HEAD) != $(git log --oneline -n1 "$S") ]] &&
       ! git rebase --continue; then
        N=$(( ${#C[@]} - slowrebase_window_sz ))
        printf '\nConflicts while rebasing $S (%s) slowly onto $T (%s)\n' "$S" 
"$T"
        printf '${C[@]} has the commits left to process (%s left)\n' $N
        printf '$B is the commit we are rebasing onto right now: %s\n' "$B"
        printf '$b is the previous commit we had already rebased onto: %s\n' 
"$b"
        return 1
    fi

    while ((${#C[@]} > 0)); do
        printf '%s commits left\n' ${#C[@]}
        N=$(( ${#C[@]} - slowrebase_window_sz ))
        b=$B
        B=${C[$N]}
        printf 'Rebasing onto %s\n' "$(git log --oneline -n1 "$B")"
        if git rebase --onto "$B" "$b" "$S"; then
            # No conflicts.  Let's go faster if we can.
            if ((slowrebase_window_sz < N)); then
                ((slowrebase_window_sz++))
            fi
            C=(${C[@]:0:$N})
            continue
        fi

        # We have conflicts; bisect if we can
        if ((slowrebase_window_sz > 1)); then
            # Bisect to find the first commit causing the conflicts
            ((slowrebase_window_sz = (slowrebase_window_sz + 1) / 2))
            git rebase --abort
            continue
        fi

        # Finally, we have a commit causing conflicts.  The user has to
        # resolve and invoke this function again.
        unset C[$N]

        printf '\nConflicts while rebasing $S (%s) slowly onto $T (%s)\n' "$S" 
"$T"
        printf '${C[@]} has the commits left to process (%s left)\n' ${#C[@]}
        printf '$B is the commit we are rebasing onto right now: %s\n' "$B"
        printf '$b is the previous commit we had already rebased onto: %s\n' 
"$b"
        return 1
    done

    printf '\n\nDONE!\n'
    return 0
}

Reply via email to