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

Implement LAST_INSERT_IDS function #1469

Conversation

laurynas-biveinis
Copy link
Contributor

@laurynas-biveinis laurynas-biveinis commented Jun 17, 2024

This is a multiple-value counterpart of the existing LAST_INSERT_ID function.
Its results are returned as a table.

  • Hardcode the function name LAST_INSERT_IDS into the lexer and the parser due
    to how MySQL supports table-returning functions, patch the parser for the
    function.
  • To keep the autoincrement value set efficiently, implement a data structure
    that 1) uses delta encoding for consecutive values; 2) special-cases encoding
    of consecutive delta equal to one values into a single number.
  • Maintain the last_insert_ids set in THD for the previous and the current
    statement, similar to existing first_successful_insert_id_in_prev_stmt and
    first_successful_insert_id_in_cur_stmt fields.
  • Add a main.last_insert_ids test.
  • Since MyISAM is an engine that supports composite primary keys with
    autoincrement suffixes, this scenario is supported as well by allowing the
    values to be actually non-monotonic by delta encoding writing large deltas
    that make the value wrap around. For this case add a
    main.last_insert_ids_myisam test.

@laurynas-biveinis laurynas-biveinis force-pushed the last_insert_ids branch 2 times, most recently from 72bf3b9 to 5fd1a67 Compare July 8, 2024 12:02
@laurynas-biveinis laurynas-biveinis force-pushed the last_insert_ids branch 2 times, most recently from 7b83325 to 047f48a Compare July 12, 2024 13:41
@laurynas-biveinis laurynas-biveinis marked this pull request as ready for review July 12, 2024 19:25
@facebook-github-bot
Copy link

@hermanlee has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@hermanlee
Copy link
Contributor

I haven't gotten very far with the review yet, but I ran a few experiments and hit an assertion with the following backtrace:

(gdb) bt                                                                                                                                                                                    [0/328]
#0  __pthread_kill_internal (signo=6, threadid=<optimized out>) at pthread_kill.c:45
#1  __GI___pthread_kill (threadid=<optimized out>, signo=6) at pthread_kill.c:62
#2  0x00007ffff7c444ad in __GI_raise (sig=6) at ../sysdeps/posix/raise.c:26                                                                                                                        
#3  0x00007ffff7c2c433 in __GI_abort () at abort.c:79                                                                                                                                              
#4  0x00007ffff7c3bc28 in __assert_fail_base (fmt=0x7ffff7de11d8 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", assertion=0x491b859 "(next_lowest_byte & RUN_OF_ONES_MASK) == is_run_of_ones_length || number == 0U", file=0x3bf67bb "/home/herman/rocks-mysql/8.0/sql/compressed_uint64_fifo.h", line=94, function=<optimized out>) at assert.c:92                                                     
#5  0x00007ffff7c3bc93 in __GI___assert_fail (assertion=0x491b859 "(next_lowest_byte & RUN_OF_ONES_MASK) == is_run_of_ones_length || number == 0U", file=0x3bf67bb "/home/herman/rocks-mysql/8.0/sql/compressed_uint64_fifo.h", line=94, function=0x3ea1255 "std::pair<std::uint64_t, bool> compressed_uint64_fifo::const_iterator::read()") at assert.c:101                                          
#6  0x00000000069a15ea in compressed_uint64_fifo::const_iterator::read (this=0x7ffff63f47b0) at /home/herman/rocks-mysql/8.0/sql/compressed_uint64_fifo.h:93                                       
#7  0x00000000069a146b in compressed_uint64_fifo::const_iterator::advance (this=0x7ffff63f47b0) at /home/herman/rocks-mysql/8.0/sql/compressed_uint64_fifo.h:123                                   
#8  0x00000000069a0fc1 in compressed_uint64_fifo::const_iterator::operator++ (this=0x7ffff63f47b0) at /home/herman/rocks-mysql/8.0/sql/compressed_uint64_fifo.h:159                                
#9  0x00000000069a029c in Table_function_last_insert_ids::fill_result_table (this=0x7ffe7863c440) at /home/herman/rocks-mysql/8.0/sql/table_function.cc:801                                        
#10 0x000000000623da08 in MaterializedTableFunctionIterator::Init (this=0x7ffe777eae20) at /home/herman/rocks-mysql/8.0/sql/iterators/composite_iterators.cc:1979                                  
#11 0x00000000069070cb in Query_expression::ExecuteIteratorQuery (this=0x7ffe7863b080, thd=0x7ffe7860b000) at /home/herman/rocks-mysql/8.0/sql/sql_union.cc:1763                                   
#12 0x0000000006907762 in Query_expression::execute (this=0x7ffe7863b080, thd=0x7ffe7860b000) at /home/herman/rocks-mysql/8.0/sql/sql_union.cc:1825                                                
#13 0x000000000681c990 in Sql_cmd_dml::execute_inner (this=0x7ffe777e9848, thd=0x7ffe7860b000) at /home/herman/rocks-mysql/8.0/sql/sql_select.cc:868                                               
#14 0x000000000681bcd6 in Sql_cmd_dml::execute (this=0x7ffe777e9848, thd=0x7ffe7860b000) at /home/herman/rocks-mysql/8.0/sql/sql_select.cc:616                                                     
#15 0x00000000067505d7 in mysql_execute_command (thd=0x7ffe7860b000, first_level=true, last_timer=0x7ffff63f91e0) at /home/herman/rocks-mysql/8.0/sql/sql_parse.cc:5498                            
#16 0x00000000067452da in dispatch_sql_command (thd=0x7ffe7860b000, parser_state=0x7ffff63f9ac0, last_timer=0x7ffff63f91e0) at /home/herman/rocks-mysql/8.0/sql/sql_parse.cc:6285                  
#17 0x000000000673ffc3 in dispatch_command (thd=0x7ffe7860b000, com_data=0x7ffff63faad0, command=COM_QUERY) at /home/herman/rocks-mysql/8.0/sql/sql_parse.cc:2566                                  
#18 0x000000000674371f in do_command (thd=0x7ffe7860b000) at /home/herman/rocks-mysql/8.0/sql/sql_parse.cc:1746                                                                                    
#19 0x0000000006a470e6 in handle_connection(void*) [clone .__uniq.225727456442500944644245527217620215556] (arg=0x7ffeb6a31520) at /home/herman/rocks-mysql/8.0/sql/conn_handler/connection_handler_per_thread.cc:307                                                                                                                                                                                 
#20 0x0000000008c5fe67 in pfs_spawn_thread(void*) [clone .__uniq.309927101143458076510661553731877978615] (arg=0x7ffeb6be8120) at /home/herman/rocks-mysql/8.0/storage/perfschema/pfs.cc:3022      
#21 0x00007ffff7c9abc9 in start_thread (arg=<optimized out>) at pthread_create.c:434                                                                                                               
#22 0x00007ffff7d2cf5c in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

To reproduce:

create table t1 (pk int auto_increment, primary key (pk)) engine=rocksdb;

Test script

insert into t1 values (), (), (), ..., (); # Insert 64K rows
select * from last_insert_ids() as ids;

Run the test script concurrently against itself, i.e. two connections each inserting 64K rows and selecting last_insert_ids() after.

@laurynas-biveinis
Copy link
Contributor Author

Reproduced with myqslslap, two threads, inserting 128 rows at a time

@laurynas-biveinis
Copy link
Contributor Author

laurynas-biveinis commented Aug 5, 2024

It seems there is no bug, but the assert is written incorrectly. I was expecting the left side of (next_lowest_byte & RUN_OF_ONES_MASK) == is_run_of_ones_length) to implicitly convert to bool, but here we have (next_lowest_byte & RUN_OF_ONES_MASK) == 128, is_run_of_ones_length == true, and these two compare non-equal. Trying with explicit cast to bool.

@facebook-github-bot
Copy link

@laurynas-biveinis has updated the pull request. You must reimport the pull request before landing.

@laurynas-biveinis
Copy link
Contributor Author

Adjusted the assert, removed unused iterator fields, rebased

@facebook-github-bot
Copy link

@hermanlee has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@hermanlee
Copy link
Contributor

hermanlee commented Aug 5, 2024

Can you add a test that tests the concurrency of the code and validates the list of IDs matches the values generated by the query?

You can probably do this via mysqlslap where each client does the following:

begin;
insert into t1 ...; # 128 rows
commit;
select count(*) = 128 from t1 join last_insert_ids() as ids on t1.pk = ids.pk;
drop table t1_temp;

begin;
insert into t1 ...; # 128 rows
rollback;
select count(*) = 0 from t1 join last_insert_ids() as ids on t1.pk = ids.pk;

Run 4 threads of each transaction, or maybe dedicate some threads to committing transactions rows and others to rolling them back.

Copy link
Contributor

@hermanlee hermanlee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good. Just some requests for extra test and additional assertions.

if (delta == 1) {
++current_run_of_ones_len;
} else {
if (current_run_of_ones_len > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can probably make this } else if (current_run_of_ones_len > 0) { and not need the additional level of indentation.

Alternatively, to dedup the data.reserve() and write(delta), you could have this conditional only process the run_of_ones data. It might be more efficient to call data.reserve() with larger counts. inserts tend to be concurrent and if the increments that are not delta = 1 ends up calling reserve significantly. Maybe initialize reserve 128B and then let the system reserve as needed? This simplifies the code to:

  } else {
    if (current_run_of_ones_len > 0) {
      write(current_run_of_ones_len, true);
      current_run_of_ones_len = 0;
    }
    write(delta, false);
  }

Copy link
Contributor Author

@laurynas-biveinis laurynas-biveinis Aug 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I worried that unconditional reserve(128) will be wasteful in too many cases, where we only have a single value inserted and when the deltas are only equal to 1, and we don't touch the vector at all. Maybe we can put reserve(128) into this else branch. The first time it runs, it will do that, and will be a no-op afterwards.

is_run_of_ones_length ||
number == 0U);
if (!continues)
is_run_of_ones_length = next_lowest_byte & RUN_OF_ONES_MASK;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe assert(is_runs_of_ones_length == !!(next_lowest_byte & RUN_OF_ONES_MASK)) in the case of continues is true? The write path sets the ones_mask for all bytes, or for none of the bytes.

}

void advance() noexcept {
if (stream_itr == container.data.end()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment that the last bytes stored in the container is never a run-of-ones, so that if we are at the end of the container, then remaining_run_of_ones_len should be 0?

Also, maybe assert (remaining_runs_of_ones_len == 0);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, also added an assert that it's not the end at the if (is_run_of_ones_length) below

number |= (next_lowest_byte << next_byte_pos);
next_byte_pos += DATA_PER_BYTE_LEN;
} while (continues);
assert(number > 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also assert that if this is run_of_ones, then there should be more data in the container because the end of the container should never be a run_of_ones.

@facebook-github-bot
Copy link

@laurynas-biveinis has updated the pull request. You must reimport the pull request before landing.

@laurynas-biveinis
Copy link
Contributor Author

@hermanlee , addressed all review comments and rebased

@laurynas-biveinis
Copy link
Contributor Author

It just occurred to me that the 2nd and further bytes in each multibyte-encoded number can use 7 instead of 6 bits for data for better compression, but I'm not sure it's worth it

@facebook-github-bot
Copy link

@hermanlee has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

Copy link
Contributor

@hermanlee hermanlee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm seeing an assert on ubsan builds when running the stress test:

----------SERVER LOG START-----------
/data/sandcastle/boxes/trunk-git-mysql/sql/compressed_uint64_fifo.h:94:37: runtime error: shift exponent 36 is too large for 32-bit type 'int'
#0 0x89bd175 in compressed_uint64_fifo::const_iterator::read() /data/sandcastle/boxes/trunk-git-mysql/sql/compressed_uint64_fifo.h:94:37
#1 0x89bd020 in compressed_uint64_fifo::const_iterator::advance() /data/sandcastle/boxes/trunk-git-mysql/sql/compressed_uint64_fifo.h:120:52
#2 0x89bc9fd in compressed_uint64_fifo::const_iterator::operator++() /data/sandcastle/boxes/trunk-git-mysql/sql/compressed_uint64_fifo.h:153:7
#3 0x89bc8fb in Table_function_last_insert_ids::fill_result_table() /data/sandcastle/boxes/trunk-git-mysql/sql/table_function.cc:801:33
#4 0x837e70f in MaterializedTableFunctionIterator::Init() /data/sandcastle/boxes/trunk-git-mysql/sql/iterators/composite_iterators.cc:1979:25
#5 0x8936090 in Query_expression::ExecuteIteratorQuery(THD*) /data/sandcastle/boxes/trunk-git-mysql/sql/sql_union.cc:1763:26
#6 0x8936577 in Query_expression::execute(THD*) /data/sandcastle/boxes/trunk-git-mysql/sql/sql_union.cc:1825:10
#7 0x8861e8d in Sql_cmd_dml::execute_inner(THD*) /data/sandcastle/boxes/trunk-git-mysql/sql/sql_select.cc:868:15
#8 0x8860e3b in Sql_cmd_dml::execute(THD*) /data/sandcastle/boxes/trunk-git-mysql/sql/sql_select.cc:616:9
#9 0x87aaa4d in mysql_execute_command(THD*, bool, unsigned long long*) /data/sandcastle/boxes/trunk-git-mysql/sql/sql_parse.cc:5498:29
#10 0x87a664d in dispatch_sql_command(THD*, Parser_state*, unsigned long long*) /data/sandcastle/boxes/trunk-git-mysql/sql/sql_parse.cc:6285:21
#11 0x87a2b79 in dispatch_command(THD*, COM_DATA const*, enum_server_command) /data/sandcastle/boxes/trunk-git-mysql/sql/sql_parse.cc:2566:7
#12 0x87a49d0 in do_command(THD*) /data/sandcastle/boxes/trunk-git-mysql/sql/sql_parse.cc:1746:18
#13 0x8a53cc3 in handle_connection(void*) (.__uniq.199618011249897873951969475570011787283) /data/sandcastle/boxes/trunk-git-mysql/sql/conn_handler/connection_handler_per_thread.cc:307:13
#14 0xa38dff3 in pfs_spawn_thread(void*) (.__uniq.62711780575692142442886335758301887438) /data/sandcastle/boxes/trunk-git-mysql/storage/perfschema/pfs.cc:3022:3
#15 0x7f3c85a9abc8 in start_thread /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/nptl/pthread_create.c:434:8
#16 0x7f3c85b2cf5b in __GI___clone3 /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /data/sandcastle/boxes/trunk-git-mysql/sql/compressed_uint64_fifo.h:94:37 in
safe_process[449604]: Child process: 449609, exit: 1


--let $count=200
--let $table=clients_finished
--let $wait_timeout=60
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I needed a larger timeout for asan debug builds. Can you set this to 600?

@laurynas-biveinis
Copy link
Contributor Author

sql/compressed_uint64_fifo.h:94:37: runtime error: shift exponent 36 is too large for 32-bit type 'int' #0 0x89bd175 in compressed_uint64_fifo::const_iterator::read()

Fixing, next_lowest_byte must be same width as the final number

This is a multiple-value counterpart of the existing LAST_INSERT_ID function.
Its results are returned as a table.

- Hardcode the function name LAST_INSERT_IDS into the lexer and the parser due
  to how MySQL supports table-returning functions, patch the parser for the
  function.
- To keep the autoincrement value set efficiently, implement a data structure
  that 1) uses delta encoding for consecutive values; 2) special-cases encoding
  of consecutive deltas equal to one into a single number.
- Maintain the last_insert_ids set in THD for the previous and the current
  statement, similar to existing first_successful_insert_id_in_prev_stmt and
  first_successful_insert_id_in_cur_stmt fields.
- Add main.last_insert_ids and main.last_insert_ids_stress tests.
- Since MyISAM is an engine that supports composite primary keys with
  autoincrement suffixes, this scenario is supported as well by allowing the
  values to be actually non-monotonic by delta encoding writing large deltas
  that make the value wrap around. For this case add a
  main.last_insert_ids_myisam test.
@laurynas-biveinis
Copy link
Contributor Author

Addressed both review comments, ready for review again (no rebase this time because #1488 hasn't landed yet)

@facebook-github-bot
Copy link

@laurynas-biveinis has updated the pull request. You must reimport the pull request before landing.

@facebook-github-bot
Copy link

@hermanlee has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@hermanlee
Copy link
Contributor

I think there might be one more location that needs to be updated:

/data/sandcastle/boxes/trunk-git-mysql/sql/compressed_uint64_fifo.h:100:37: runtime error: shift exponent 36 is too large for 32-bit type 'int'
    #0 0x89bd275 in compressed_uint64_fifo::const_iterator::read() /data/sandcastle/boxes/trunk-git-mysql/sql/compressed_uint64_fifo.h:100:37
    #1 0x89bd120 in compressed_uint64_fifo::const_iterator::advance() /data/sandcastle/boxes/trunk-git-mysql/sql/compressed_uint64_fifo.h:123:52
    #2 0x89bcadd in compressed_uint64_fifo::const_iterator::operator++() /data/sandcastle/boxes/trunk-git-mysql/sql/compressed_uint64_fifo.h:159:7
    #3 0x89bc9bb in Table_function_last_insert_ids::fill_result_table() /data/sandcastle/boxes/trunk-git-mysql/sql/table_function.cc:801:33
    #4 0x837e70f in MaterializedTableFunctionIterator::Init() /data/sandcastle/boxes/trunk-git-mysql/sql/iterators/composite_iterators.cc:1979:25
    #5 0x8936140 in Query_expression::ExecuteIteratorQuery(THD*) /data/sandcastle/boxes/trunk-git-mysql/sql/sql_union.cc:1763:26
    #6 0x8936627 in Query_expression::execute(THD*) /data/sandcastle/boxes/trunk-git-mysql/sql/sql_union.cc:1825:10
    #7 0x8861f3d in Sql_cmd_dml::execute_inner(THD*) /data/sandcastle/boxes/trunk-git-mysql/sql/sql_select.cc:868:15
    #8 0x8860eeb in Sql_cmd_dml::execute(THD*) /data/sandcastle/boxes/trunk-git-mysql/sql/sql_select.cc:616:9
    #9 0x87aaafd in mysql_execute_command(THD*, bool, unsigned long long*) /data/sandcastle/boxes/trunk-git-mysql/sql/sql_parse.cc:5498:29
    #10 0x87a66fd in dispatch_sql_command(THD*, Parser_state*, unsigned long long*) /data/sandcastle/boxes/trunk-git-mysql/sql/sql_parse.cc:6285:21
    #11 0x87a2c29 in dispatch_command(THD*, COM_DATA const*, enum_server_command) /data/sandcastle/boxes/trunk-git-mysql/sql/sql_parse.cc:2566:7
    #12 0x87a4a80 in do_command(THD*) /data/sandcastle/boxes/trunk-git-mysql/sql/sql_parse.cc:1746:18
    #13 0x8a53df3 in handle_connection(void*) (.__uniq.199618011249897873951969475570011787283) /data/sandcastle/boxes/trunk-git-mysql/sql/conn_handler/connection_handler_per_thread.cc:307:13
    #14 0xa38e0d3 in pfs_spawn_thread(void*) (.__uniq.62711780575692142442886335758301887438) /data/sandcastle/boxes/trunk-git-mysql/storage/perfschema/pfs.cc:3022:3
    #15 0x7fc6da49abc8 in start_thread /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/nptl/pthread_create.c:434:8
    #16 0x7fc6da52cf5b in __GI___clone3 /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /data/sandcastle/boxes/trunk-git-mysql/sql/compressed_uint64_fifo.h:100:37 in safe_process[450604]: Child process: 450616, exit: 1

@laurynas-biveinis
Copy link
Contributor Author

I think there might be one more location that needs to be updated:

/data/sandcastle/boxes/trunk-git-mysql/sql/compressed_uint64_fifo.h:100:37: runtime error: shift exponent 36 is too large for 32-bit type 'int'
    #0 0x89bd275 in compressed_uint64_fifo::const_iterator::read() /data/sandcastle/boxes/trunk-git-mysql/sql/compressed_uint64_fifo.h:100:37
    #1 0x89bd120 in compressed_uint64_fifo::const_iterator::advance() /data/sandcastle/boxes/trunk-git-mysql/sql/compressed_uint64_fifo.h:123:52
    #2 0x89bcadd in compressed_uint64_fifo::const_iterator::operator++() /data/sandcastle/boxes/trunk-git-mysql/sql/compressed_uint64_fifo.h:159:7

...

SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /data/sandcastle/boxes/trunk-git-mysql/sql/compressed_uint64_fifo.h:100:37 in safe_process[450604]: Child process: 450616, exit: 1

The line numbers seem to be off - is this a run from the latest push?
(FWIW I was not able to get my UBSan to reproduce these errors)

@facebook-github-bot
Copy link

@hermanlee has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@hermanlee
Copy link
Contributor

Sorry, I think I accidentally reverted the change during the import process. Let me resolve it again.

@luqun
Copy link
Contributor

luqun commented Sep 5, 2024

mysql> CREATE TABLE t1 (pk INT AUTO_INCREMENT PRIMARY KEY);
Query OK, 0 rows affected (0.03 sec)

mysql> set sql_mode='NO_AUTO_VALUE_ON_ZERO';
Query OK, 0 rows affected (0.00 sec)

mysql>
mysql> INSERT INTO t1 VALUES(0);
Query OK, 1 row affected (0.01 sec)

mysql>
mysql> SELECT * FROM LAST_INSERT_IDS() AS ids;
Empty set (0.00 sec)

mysql> SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 0 |
+------------------+
1 row in set (0.00 sec)

These two behaviors are different.. do we need to align or?

@laurynas-biveinis
Copy link
Contributor Author

mysql> CREATE TABLE t1 (pk INT AUTO_INCREMENT PRIMARY KEY); Query OK, 0 rows affected (0.03 sec)

mysql> set sql_mode='NO_AUTO_VALUE_ON_ZERO'; Query OK, 0 rows affected (0.00 sec)

mysql> mysql> INSERT INTO t1 VALUES(0); Query OK, 1 row affected (0.01 sec)

mysql> mysql> SELECT * FROM LAST_INSERT_IDS() AS ids; Empty set (0.00 sec)

mysql> SELECT LAST_INSERT_ID(); +------------------+ | LAST_INSERT_ID() | +------------------+ | 0 | +------------------+ 1 row in set (0.00 sec)

These two behaviors are different.. do we need to align or?

I am not sure. autoincrement value of zero is forbidden, and here you are just inserting zero as a regular value. LAST_INSERT_ID returning zero is consistent with it not handling this case.

@luqun
Copy link
Contributor

luqun commented Sep 5, 2024

LAST_INSERT_IDS

"SELECT LAST_INSERT_ID();" return 1 value while "SELECT * FROM LAST_INSERT_IDS() AS ids;" return 0 value.

maybe both should return 1 value or 0 value?

@laurynas-biveinis
Copy link
Contributor Author

LAST_INSERT_IDS

"SELECT LAST_INSERT_ID();" return 1 value while "SELECT * FROM LAST_INSERT_IDS() AS ids;" return 0 value.

maybe both should return 1 value or 0 value?

LAST_INSERT_ID() returns zero in many cases (i.e. open a new connection and call it without inserting anything), here zero it returns has no relation to the zero you are inserting to the table.

@hermanlee
Copy link
Contributor

There's a lot of undefined behavior in LAST_INSERT_ID(). I don't think LAST_INSERT_IDS() returning 0 is valid though, because 0 is never an ID assigned by auto-increment.

I think we can only rely on results from LAST_INSERT_ID(), and subsequently LAST_INSERT_IDS(), where insert successfully inserted at least one row.

@facebook-github-bot
Copy link

This pull request has been merged in 00c726c.

@laurynas-biveinis laurynas-biveinis deleted the last_insert_ids branch September 9, 2024 07:39
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.

4 participants