Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add LAST_INSERT_ID to MySQL upsert query #177

Closed
wants to merge 1 commit into from

Conversation

tpisani
Copy link

@tpisani tpisani commented Jul 17, 2017

It fixes an error caused by a query that uses the last insert ID to populate default values for structs after an upsert query at https://github.com/vattle/sqlboiler/blob/master/templates/17_upsert.tpl#L200, currently using 0 as the ID:

models: unable to populate default values for <model>: sql: no rows in result set

@aarondl
Copy link
Member

aarondl commented Jul 19, 2017

So we already have functionality in there to utilize the last insert id. I get the feeling you're looking for something else. We're going to need more details on what you're trying to accomplish and what's not working in order to figure out the proper solution. At first glance this doesn't seem necessary. Can you elaborate on what's going on?

@tpisani
Copy link
Author

tpisani commented Jul 21, 2017

I was trying to do something like this:

type Team struct {
    ID         int
    ExternalID int
    Name       string
    Initials   string
}

type TeamStore struct {
    db *sql.DB
}

func (s TeamStore) SaveTeam(t *Team) error {
    o := models.Team{
        ExternalID: t.ExternalID,
        Name:       t.Name,
        Initials:   t.Initials,
    }
    err := o.Upsert(s.db, []string{"name", "initials"})
    t.ID = o.ID
    return err
}

ExternalID is a field I'm using to store a unique ID from a third-party API.

But it kept returning this error:

models: unable to populate default values for teams: sql: no rows in result set

I noticed that the value of o.ID was 0, then I checked the docs for something that could fill the last insert ID correctly but I failed to find a way to do it. So I decided to take a look at the upsert query and it looked like this:

INSERT INTO teams (`external_id`, `name`, `initials`) VALUES (?,?,?) ON DUPLICATE KEY UPDATE `name` = VALUES(`name`),`initials` = VALUES(`initials`)

And the query for updating default values that runs immediately after looked like this:

SELECT `id` FROM `teams` WHERE `id`=?
0

Then I tried to modify the query to "return" the last insert ID by adding id = LAST_INSERT_ID(id), which sets id to the current ID if an update has occurred.

@aarondl
Copy link
Member

aarondl commented Jul 27, 2017

@tpisani Sorry I was on vacation. Back now :)

So the code I mentioned before is supposed to do this by getting the last_insert_id from the mysql driver. Maybe you're using a mysql driver that doesn't support it? Can you tell me which driver you're using?

It's possible it has a bug, but again I don't think Pull Request isn't the answer.

Is it possible you can give me the mysql schema involved here for the Team model?

@tpisani
Copy link
Author

tpisani commented Jul 27, 2017

It's OK. 😉

Maybe the problem is the driver I'm using then: https://github.com/go-sql-driver/mysql.

Here is the create statement for teams table:

CREATE TABLE `teams` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `external_id` int(11) NOT NULL,
  `name` varchar(100) NOT NULL,
  `initials` varchar(10) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `external_id` (`external_id`)
);

Thank you for your time and patience.

@aarondl aarondl self-assigned this Nov 16, 2017
aarondl added a commit that referenced this pull request Nov 17, 2017
- What ends up happening in this bug is that if there's a second unique
  column MySQL automatically allows upsert collisions on it, this means
  that if you have a unique column that was not the id break, and you
  also didn't have the ID in your struct, lookup of the values would
  fail (looking up default values), which happens because your struct
  doesn't have the ID value.
- This patch changes it such that it now uses a list of all non-zero
  unique values as a way to determine what should be used to pull back
  the value in the case of a collision where the lastinsertid fails us.
- Fix #177
@aarondl aarondl added this to the v3 milestone May 14, 2018
aarondl added a commit that referenced this pull request Jun 11, 2018
A long long time ago, someone opened PR #177 trying to resolve an issue
with mysql upsert. I made a fix, but I never actually pulled it into any
mainline code for some reason (commit 2372335). This is the same change
made to the v3 code.

The reason for this change is because mysql can conflict automatically
during upsert on any unique column. This makes total sense. However if
it does happen to conflict on an insert for which we don't have an ID
(which is probably all the time), then when we go to retrieve the record
it fails because we don't have an id and we try to look it up using said
id. Instead now we look for non-zero unique columns as the way to find
the record (which includes the id if it's non-zero).

- Fix mysql upsert bug reported in PR #177
@aarondl
Copy link
Member

aarondl commented Jun 11, 2018

Fixed in v3 branch. Sorry for the year delay :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants