-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Expose last_insert_id from Ok packet on MySQL #4271
base: master
Are you sure you want to change the base?
Changes from all commits
6062345
a907165
edb451d
c6639ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -160,6 +160,11 @@ impl<'a> StatementUse<'a> { | |
.map_err(|e| Error::DeserializationError(Box::new(e))) | ||
} | ||
|
||
/// SAFETY: This should only be called for INSERT queries. | ||
pub(in crate::mysql::connection) unsafe fn insert_id(&self) -> u64 { | ||
ffi::mysql_stmt_insert_id(self.inner.stmt.as_ptr()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The mysql documentation states that:
https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-insert-id.html Given that it is possible to have insert statements that do not have a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While that's how the documentation is written, I don't think that the result is UB. The value is always present in the Ok packet and will always be a valid byte sequence for a u64. The value absolutely might not make any sense if no Looking at what I also, FWIW, don't have any idea what kind of check we'd be able to perform in a query context for that beyond possibly matching that 0 check There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm still wary to relay on something that's explicitly documented as not being defined as that could change at any point in the future. Remember that we want to provide a reasonable level of stability that should be decoupled from our dependencies. So yes, it might not be undefined behavior in terms of programming to read this value, the result still stays undefined and might change at any point. It's something that at least I do not feel comfortable with exposing in a stable API. What we can easily do is to restrict it to insert statements with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another way to quickly get this feature is expose it under some unstable feature flag, if you not against that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would rather prefer restricting it at least to Integer primary keys, as that isn't much harder than marking it as unstable feature. After all that should just require one or two lines of trait bounds on the relevant method. |
||
} | ||
|
||
/// This function should be called after `execute` only | ||
/// otherwise it's not guaranteed to return a valid result | ||
pub(in crate::mysql::connection) unsafe fn result_size(&mut self) -> QueryResult<usize> { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
use self::private::LoadIter; | ||
use super::RunQueryDsl; | ||
use crate::backend::Backend; | ||
use crate::connection::{Connection, DefaultLoadingMode, LoadConnection}; | ||
use crate::connection::{ | ||
Connection, ConnectionWithReturningId, DefaultLoadingMode, LoadConnection, | ||
}; | ||
use crate::deserialize::FromSqlRow; | ||
use crate::expression::QueryMetadata; | ||
use crate::query_builder::{AsQuery, QueryFragment, QueryId}; | ||
|
@@ -91,6 +93,34 @@ where | |
} | ||
} | ||
|
||
/// The `execute_with_returning_id` method | ||
/// | ||
/// This is currently only implemented for [`MySQL`], because MySQL provides a | ||
/// `last_insert_id` field as part of its response packet to `INSERT` queries, but | ||
/// it's in a trait to avoid a backwards-incompatible change in the future if such | ||
/// functionality becomes available in other back-ends. | ||
/// | ||
/// [`MySQL`]: crate::mysql::Mysql | ||
pub trait ExecuteInsertWithReturningId< | ||
Conn: ConnectionWithReturningId<Backend = DB>, | ||
DB: Backend = <Conn as Connection>::Backend, | ||
>: Sized | ||
{ | ||
/// Execute this command | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There should be a doc test/example on this function to show how this works There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will do |
||
fn execute_with_returning_id(query: Self, conn: &mut Conn) -> QueryResult<Conn::ReturnedId>; | ||
} | ||
|
||
impl<Conn, DB, T> ExecuteInsertWithReturningId<Conn, DB> for T | ||
where | ||
Conn: ConnectionWithReturningId<Backend = DB>, | ||
DB: Backend, | ||
T: QueryFragment<DB> + QueryId, | ||
{ | ||
fn execute_with_returning_id(query: Self, conn: &mut Conn) -> QueryResult<Conn::ReturnedId> { | ||
conn.execute_returning_id(&query) | ||
} | ||
} | ||
|
||
// These types and traits are not part of the public API. | ||
// | ||
// * CompatibleType as we consider this as "sealed" trait. It shouldn't | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be a
Safety: explain how the invariants of the called function are uphold
comment here.Additionally as far as I can see the
insert_id()
function states that it is only safe to call this function on insert statements, while we can call it on arbitrary statements here. That sounds like the current implementation is unsound.In the end we want to replace the
T: QueryFragement<Self::Backend>
bound above with essentiallyInsertStatement
to restrict the allowed statements to only insert statements.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do