#postgresql multi inserts return order
96 messages · Page 1 of 1 (latest)
Just do a select for all items with those IDs afterwards?
I don't know how performance critical this is. That seems an easy answer though.
@cosmic cypress do your database tables not have unique indexes on any fields? it might help if you can give a complete/concrete example—maybe you can expand your "4 objects" scenario and explain exactly what the database and in-memory objects would look like during that process
upon re-read i think you might be saying there is a unique field (ID) but it's assigned by the database via auto-increment or somesuch?
Apparently postgres does return in order for simple queries, but it isn't guaranteed by a specification
i'm still confused, why does the order matter? if RETURNING * doesn't already give you what you want for some reason, it'll at least give you the generated IDs that you could then use to select exactly the rows you want (or run other queries as needed)
ah, ApplyValuesToEntityFromDatabaseResult(player.inventory, insertResult) has a side effect of mutating player.inventory?
So is there like a playerId + itemId combination that acts as a unique identifier?
then you can match up the results after in JS?
Can there be?
can't you use insertResult directly to decide what items to insert rather than player.inventory.items? that way you'll only be looking at inventories that were actually inserted (and not also ones that may have existed before)
So can you have
Player Item Number
1 Apple 3
1 Apple 1
?
Are items fungible?
Because if the are fungible it would seem better to require items to be combined. Then you have a unique identifier
Player + Item, that you can use to relate the input to the output
right. i perhaps still don't understand the problem (which is why i wanted a complete/concrete example), but i thought you were saying that for (const item of player.inventory.items) is looping over items that it shouldn't be. if you instead did for (const item of itemResult) or whatnot then that would solve that problem
pretty sure it is, i think i've done this before with postgres
i dunno what they meant by "delayed" earlier though. maybe batch upserts are not enough
I'm thinking make a map of player+item to entry when inserting, do the batch upsert, then loop over the result using the map to relate the result back to the input
the same item can be in multiple inventories?
i think i'm just too lost on the details. i'll jump back in if you can write it all up, otherwise i'll duck out and let @plush bough continue helping
I don't see a solution if you have to generate a unique id for a certain items, unless you can handle generating those IDs before inserting. Seems viable to make that a separate process, maybe store unique IDs in memory. Then that unique ID gives you a unique handle to relate back again
.
.
I explained that here
FWIW these days i almost always generate IDs in code and not the DB. i find it makes many things simpler, and isn't usually very expensive if you're smart about it. it also naturally helps with future scaling stuff (DB sharding, etc)
a giant uuid or a giant number aren't that different IMO
If a player can't have 2 "apple" items, and instead the query updates the "apple" row by increasing the count, then you can relate back using player ID + "apple". You might have multiple "apple" inputs, but then maybe there is no problem. I'm not actually sure of what the purpose of relating back to the inputs does, what is that for?
For fungible (non unique) items, I don't think you would need to
For unique items you can generate a unique ID before inserting
I've explained this twice. You can re-read the comments until you understand
I am talking about changing the way you handle it to solve the problem you presented
If items are unique, generate the ID before you insert, like I've said
If you have a single server you can just have an int and +1 for each item to get an ID
That's not related to anything I've been telling you
Why?
You have more than 1 process inserting?
You can give a unique prefix to different processes
So they cannot conflict
I don't know what "out of order" means, I'm not sure items have any meaningful order
Yeah...
Mirror of the official PostgreSQL GIT repository. Note that this is just a mirror - we don't work with pull requests on github. To contribute, please see https://wiki.postgresql.org/wiki/...
here's the source code
you generate the ID on your server right before insert (at least that's what i do). there's a separate type used for the values needed to specify an item to be created (which may be different from the item itself in more ways than just lacking an ID). ItemSpec vs ItemEntity or whatever
If you think updating postgres source and maintaining your own fork that has consistent return ordering is easier, than be my guest. Let me know how it goes.
though i'm still not sure if this is the right scenario. your original question seemed more about transferring items between inventories/from the world into an inventory than creating new items, in which case i'd assume there's already an ID on the item
i don't think i grok what the first query is doing anyway. the concept of "creating an inventory" detached from player creation confounds me a bit. is it like when the player buys a bag or something?
Usually the player inventory is their space that holds their items
So it just means "player 1 picked up 3 apples, register these item to the player in the database"
The batching is a bit confusing to me though.
right, and that's why i'd expect the player's inventory to be created when the player itself is created. not when they pick up an (their first?) item
Where is the inventory creation?
INSERT INTO inventories? but again i'm just guessing here
Ah
maybe this is intended to fail if the entry already exists
So it's just lazy creation
But, that would be a little strange
const [insertResult] = await sql<PlayerInventory[]>`INSERT INTO inventories ${sql(player.inventory, GetDatabaseColumnsOfEntity(PlayerInventory))} RETURNING *`;
ApplyValuesToEntityFromDatabaseResult(player.inventory, insertResult);
Maybe it's like saving the state of the player's inventory? I don't know without xor's input
¯_(ツ)_/¯
Maybe I scared him off
he's busy patching postgres 🚎
💡
my bad, i missed that. so players are created with arbitrary items already present in their inventory? and there's a possibility to (re-)create the same player more than once and that needs to be handled gracefully?
So the new player gets certain starting items? Does it vary depending on their character choice? It seems to be that if a player is created with 3 shirts, and each shirt has a unique ID, it really doesn't matter which of the shirts from the input map to which if the shirts that were created. As this is player creation those shirts should all be equivalent.
After they've been created with a unique ID and allowed out into the world, maybe one will take damage, and then uniqueness matters.
Probably the result of the insert is assigned to the player as their inventory, and they aren't allowed to interact with the items at all before the insert. If you have a kind of lazy creation and they can start interacting first then of course you have an issue. But doesn't seem likely something that is really necessary.
Is there any reason why you need to have multiple "shirt" entries for a player, with a count for each entry?
There are 2 simple ways to do it: A) separate entry for each shirt B) only one entry that keeps the count of all shirts
Do you have something like stacks, where you could have a stack of 2, and a stack of 1?
Also your result contains the number in each entry if you do it that way, so again why does it need to reference the input?
Yeah I see
So just assign the result to the player. That contains all the info. Any reason you need to match the result rows to the input?
Right
So what's the reason that you need to relate it to the original stack? Is it something like player 1 picks up a stack of 3 apples, and puts them in position 8
then player 1 removes that stack before the database replies
so you want to match it up?
Options are to either not let the player interact with the stack or choose where it goes until it have been processed by the server and assigned an ID
Or assign it an ID on the client, and then use that for tracking.
I think there will only be 1 client process
Do you not actually need this, you just want to know a way to do it because it bugs you?
I asked you why you need to know which input object matches up to each inserted row
You seem to kind of avoid the questions, and keep going back to this issue of just matching up the input to the result, rather than talking about the actual problem in general
Yeah, I put that here a while ago, or something similar
.
So, can you answer, why do you need to match up the original item to the created row?
I see
I feel that you really need to assign unique IDs on the client then
It's pretty simple. Give a clientId, save that in the database, and use that to map back to the original thing.
It should be OK to make sure clientIds are always unique. They only need to be unique to that player, not to other players, because you can also check the playerId