Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@LegendBegins
Copy link
Collaborator

@LegendBegins LegendBegins commented Jun 15, 2025

image

Completely updated to account for the recent refactor. Works on local instance. Includes new sql schema.

@LegendBegins
Copy link
Collaborator Author

LegendBegins commented Jun 15, 2025

This has the commits from yesterday's auto-convert IC to OOC. I'm git-incompetent, so I don't know how to exclude them. Feel free to leave those out.

Affected Commits:
377250b
38c206d
c83ce1d
cd6fefc

@danneu
Copy link
Owner

danneu commented Jun 16, 2025

fwiw, create new branches from master so that your branches are isolated from each other. and for your own sanity.

since you already have commits in the AccountSwitcher branch, you can learn the interactive rebase, one of the only essential git tools imo:

git checkout AccountSwitcher
# back up branch in case you mess it up
git branch account-switcher2
git rebase -i master

Then in the editor that pops up, you can remove the commits from the ic/ooc branch and only keep the commits unique to AccountSwitcher.

You can automate this with git rebase --onto master UnwantedBranch AccountSwitcher but i usually just use interactive rebase if the commit count isn't crazy.

@danneu
Copy link
Owner

danneu commented Jun 16, 2025

Also, we need to deprecate adding routes to server/index.ts. let's put them in server/routes/alts.ts

@LegendBegins
Copy link
Collaborator Author

I think I've decided I hate git ;)

Changes made, thanks @danneu. I think this should be good to go.

@danneu
Copy link
Owner

danneu commented Jun 17, 2025

The owner_id terminology seems like it's unneeded since the code indicates that owner_id is only used to cluster linked users together. (root owner user_id = owner_id is never used, and one-way ownership chains don't seem to be supported)

I think we can simplify it into:

create table alt_group (
  id serial primary key
  created_at timestamptz not null default now()
)

alter table users add column alt_group_id int null references alt_group (id);

Now linking is just group merge, and unlinking is just user.alt_group = null.

Or am I not reading the alts sql correctly?

@LegendBegins
Copy link
Collaborator Author

Right, and one-way ownership chains shouldn't be supported (IMO).

Yeah, creating ownership groups is probably a more obvious solution. I think that's logically equivalent to what it's already doing: just pre-seeding an ownership group with the user's own ID. I think my motivation when I originally wrote the code was just to not modify the users table.

@LegendBegins
Copy link
Collaborator Author

LegendBegins commented Jun 18, 2025

Looking into your suggested schema, it looks like we'd need pretty complex conditionals to manage the different cases

case 1: both users are in different alt groups and we have to merge them
case 2: One user is in an alt group and the other is null
case 3: Neither user is in an alt group

So a function like linkUserAlts would look something sort of like

  const user1_alt_group = await pool.query(`SELECT alt_group_id FROM users WHERE id=$1`,[userId]).then(maybeOneRow)['alt_group_id'];
  const user_2_alt_group = await pool.query(`SELECT alt_group_id FROM users WHERE id=$1`,[altId]).then(maybeOneRow)['alt_group_id'];
  if(!user1_alt_group && !user2_alt_group)    //Neither user has an existing alt group
  { 
    const new_group_id = await pool.query(`INSERT INTO alt_group DEFAULT VALUES returning id`).then(exactlyOneRow)['id'];
    return pool.query(`UPDATE users SET alt_group_id=$1 WHERE id=$2 OR id=$3`, [new_group_id, userId, altId]);
  }
  //Otherwise, pick one of the alt groups and set all IDs to that group
  const target_group = Math.max(user1_alt_group, user2_alt_group);    //Prevents setting the group ID to a null value
  return pool.query(`UPDATE users SET alt_group_id=$1 WHERE id=$2 OR id=$3 OR ((alt_group_id=$4 OR alt_group_id=$5) AND alt_group_id IS NOT NULL))`, [target_group, userId, altId, user1_alt_group, user2_alt_group]);
}

Maybe I'm overlooking a much simpler solution, but although the schema is a little more intuitive, the implementation seems a lot less elegant. Compared to its current implementation:

  return pool.query(`
    UPDATE alts
    SET owner_id = (SELECT owner_id from alts WHERE id=$1)
    WHERE owner_id = (SELECT owner_id FROM alts WHERE id = $2)`,
    [userId, altId]);
};```

@danneu
Copy link
Owner

danneu commented Jun 18, 2025

Yeah, the difference with my schema is:

  1. Simpler mental model
  2. Unlinking is simpler: set alt_group_id = null and no arbitrary owner reselection
  3. Queries are cleaner where alt_group_id = $1
  4. Slightly more complicated link logic

Linking is a lil more complicated since it handles these cases:

Case User A group_id User B group_id Action
1. No groups NULL NULL Create new group
2. A has group 123 NULL Add B to A's group
3. B has group NULL 456 Add A to B's group
4. Both have groups 123 456 Merge groups

But you can do that in one query:

WITH ensure_group AS (
  -- Ensure user1 has a group (create if needed)
  UPDATE users 
  SET alt_group_id = COALESCE(
    alt_group_id, 
    (INSERT INTO alt_groups DEFAULT VALUES RETURNING id)
  )
  WHERE id = $1
  RETURNING alt_group_id
)
-- Merge user2 and their entire group into user1's group
UPDATE users 
SET alt_group_id = (SELECT alt_group_id FROM ensure_group)
WHERE id = $2 OR alt_group_id = (SELECT alt_group_id FROM users WHERE id = $2);

So, I think this works:

async function linkUsers(pgClient: PgClient, user1: number, user2: number) {
  // Ensure user1 has a group (create if needed)
  // Merge user2 and their entire group into user1's group
  // If users are in the same group, it does a superfluous update with no change
  await pgClient.query(`
    WITH ensure_group AS (
        UPDATE users 
        SET alt_group_id = COALESCE(
            alt_group_id, 
            (INSERT INTO alt_groups DEFAULT VALUES RETURNING id)
        )
        WHERE id = $1
        RETURNING alt_group_id
    )
    UPDATE users 
    SET alt_group_id = (SELECT alt_group_id FROM ensure_group)
    WHERE id = $2 OR alt_group_id = (SELECT alt_group_id FROM users WHERE id = $2);
  `, [user1, user2])
}

@LegendBegins
Copy link
Collaborator Author

@danneu This is done now. Had to alter the linker query because COALESCE doesn't support an INSERT statement inside its parameters.

Also, does /server/reset_db need to be updated to include all of the new .sql files? I had to run those manually when setting up a fresh instance.

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.

2 participants