1--TEST--
2API vs. SQL LAST_INSERT_ID()
3--EXTENSIONS--
4mysqli
5--SKIPIF--
6<?php
7  require_once 'skipifconnectfailure.inc';
8?>
9--FILE--
10<?php
11    /*
12    CAUTION: the insert_id() API call is not supposed to return
13    the same value as a call to the LAST_INSERT_ID() SQL function.
14    It is not necessarily a bug if API and SQL function return different
15    values. Check the MySQL C API reference manual for details.
16    */
17    require_once 'connect.inc';
18
19    function get_sql_id($link) {
20        if (!($res = $link->query("SELECT LAST_INSERT_ID() AS _id"))) {
21            printf("[003] [%d] %s\n", $link->errno, $link->error);
22            return NULL;
23        }
24        $row = $res->fetch_assoc();
25        $res->close();
26
27        return $row['_id'];
28    }
29
30    if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket))
31        printf("[001] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n",
32            $host, $user, $db, $port, $socket);
33
34    if (!$link->query("DROP TABLE IF EXISTS test") ||
35        !$link->query("CREATE TABLE test (id INT auto_increment, label varchar(10) not null, PRIMARY KEY (id)) ENGINE=MyISAM") ||
36        !$link->query("INSERT INTO test (id, label) VALUES (null, 'a')")) {
37        printf("[002] [%d] %s\n", $link->errno, $link->error);
38    }
39
40    $api_id = $link->insert_id;
41    $sql_id = get_sql_id($link);
42    printf("API: %d, SQL: %d\n", $api_id, $sql_id);
43
44    if ($api_id < 1)
45        printf("[004] Expecting id > 0 got %d, [%d] %s\n", $api_id, $link->errno, $link->error)	;
46    if ($api_id != $sql_id)
47        printf("[005] SQL id %d should be equal to API id %d\n", $sql_id, $api_id);
48
49    // Not an INSERT, API value must become 0
50    if (!($res = $link->query("SELECT 1 FROM DUAL")))
51        printf("[006] [%d] %s\n", $link->errno, $link->error);
52    else
53        $res->close();
54
55    $api_id = $link->insert_id;
56    $new_sql_id = get_sql_id($link);
57    if (0 !== $api_id) {
58        printf("[007] API id should have been reset to 0 because previous query was SELECT, got API %d, SQL %d\n",
59          $api_id, $new_sql_id);
60    }
61    if ($new_sql_id != $sql_id) {
62        printf("[008] The servers LAST_INSERT_ID() changed unexpectedly from %d to %d\n", $sql_id, $new_sql_id);
63    }
64
65    // Insert fails, LAST_INSERT_ID shall not change, API shall return 0
66    if ($link->query("INSERT INTO test (id, label) VALUES (null, null)")) {
67        printf("[009] The INSERT did not fail as planned, [%d] %s\n", $link->errno, $link->error);
68    }
69    $api_id = $link->insert_id;
70    $new_sql_id = get_sql_id($link);
71
72    if (0 !== $api_id) {
73        printf("[010] API id should have been reset to 0 because previous query was SELECT, got API %d, SQL %d\n",
74          $api_id, $new_sql_id);
75    }
76    if ($new_sql_id != $sql_id) {
77        printf("[011] The servers LAST_INSERT_ID() changed unexpectedly from %d to %d\n", $sql_id, $new_sql_id);
78    }
79
80    // Sequence counter pattern...
81    if (!$link->query("UPDATE test SET id=LAST_INSERT_ID(id+1)"))
82      printf("[012] [%d] %s\n", $link->errno, $link->error);
83
84    $api_id = $link->insert_id;
85    $new_sql_id = get_sql_id($link);
86    if ($api_id < 1)
87        printf("[013] Expecting id > 0 got %d, [%d] %s\n", $api_id, $link->errno, $link->error)	;
88    if ($api_id != $new_sql_id)
89        printf("[014] SQL id %d should be equal to API id %d\n", $new_sql_id, $api_id);
90    if ($sql_id == $new_sql_id)
91        printf("[015] SQL id %d should have had changed, got %d\n", $sql_id, $new_sql_id);
92
93    $sql_id = $new_sql_id;
94
95    // Not an INSERT (after UPDATE), API value must become 0
96    if (!$link->query("SET @myvar=1"))
97        printf("[016] [%d] %s\n", $link->errno, $link->error);
98
99    $api_id = $link->insert_id;
100    $new_sql_id = get_sql_id($link);
101    if (0 !== $api_id) {
102        printf("[017] API id should have been reset to 0 because previous query was SET, got API %d, SQL %d\n",
103          $api_id, $new_sql_id);
104    }
105    if ($new_sql_id != $sql_id) {
106        printf("[018] The servers LAST_INSERT_ID() changed unexpectedly from %d to %d\n", $sql_id, $new_sql_id);
107    }
108
109    if (!$link->query("INSERT INTO test(id, label) VALUES (LAST_INSERT_ID(id + 1), 'b')"))
110        printf("[019] [%d] %s\n", $link->errno, $link->error);
111
112    $api_id = $link->insert_id;
113    $sql_id = get_sql_id($link);
114    if ($api_id != $sql_id)
115        printf("[020] SQL id %d should be equal to API id %d\n", $sql_id, $api_id);
116
117    if (!$link->query("INSERT INTO test(label) VALUES ('c')"))
118        printf("[021] [%d] %s\n", $link->errno, $link->error);
119
120    $api_id = $link->insert_id;
121    $sql_id = get_sql_id($link);
122    if ($api_id != $sql_id)
123        printf("[022] SQL id %d should be equal to API id %d\n", $sql_id, $api_id);
124
125    if (!($res = $link->query("SELECT id, label FROM test ORDER BY id ASC")))
126        printf("[023] [%d] %s\n", $link->errno, $link->error);
127
128    printf("Dumping table contents before INSERT...SELECT experiments...\n");
129    while ($row = $res->fetch_assoc()) {
130        printf("id = %d, label = '%s'\n", $row['id'], $row['label']);
131    }
132    $res->close();
133
134    if (!$link->query("INSERT INTO test(label) SELECT CONCAT(label, id) FROM test ORDER BY id ASC"))
135      printf("[024] [%d] %s\n", $link->errno, $link->error);
136
137    $api_id = $link->insert_id;
138    $sql_id = get_sql_id($link);
139    if ($api_id != $sql_id)
140        printf("[025] SQL id %d should be equal to API id %d\n", $sql_id, $api_id);
141
142    if ($link->query("INSERT INTO test(id, label) SELECT id, CONCAT(label, id) FROM test ORDER BY id ASC"))
143      printf("[026] INSERT should have failed because of duplicate PK value, [%d] %s\n", $link->errno, $link->error);
144
145    $api_id = $link->insert_id;
146    $new_sql_id = get_sql_id($link);
147    if (0 !== $api_id) {
148        printf("[027] API id should have been reset to 0 because previous query failed, got API %d, SQL %d\n",
149          $api_id, $new_sql_id);
150    }
151    if ($new_sql_id != $sql_id) {
152        printf("[028] The servers LAST_INSERT_ID() changed unexpectedly from %d to %d\n", $sql_id, $new_sql_id);
153    }
154
155    /* API insert id will be 101 because of UPDATE, SQL unchanged */
156    if (!$link->query(sprintf("INSERT INTO test(id, label) VALUES (%d, 'z') ON DUPLICATE KEY UPDATE id = 101", $sql_id)	))
157      printf("[029] [%d] %s\n", $link->errno, $link->error);
158
159    $api_id = $link->insert_id;
160    $new_sql_id = get_sql_id($link);
161    if ($api_id != 101)
162        printf("[030] API id should be %d got %d\n", $sql_id, $api_id);
163    if ($new_sql_id != $sql_id) {
164        printf("[031] The servers LAST_INSERT_ID() changed unexpectedly from %d to %d\n", $sql_id, $new_sql_id);
165    }
166
167    if (!($res = $link->query("SELECT id, label FROM test ORDER BY id ASC")))
168        printf("[032] [%d] %s\n", $link->errno, $link->error);
169
170    printf("Dumping table contents after INSERT...SELECT...\n");
171    while ($row = $res->fetch_assoc()) {
172        printf("id = %d, label = '%s'\n", $row['id'], $row['label']);
173    }
174    $res->close();
175
176    print "done!";
177?>
178--CLEAN--
179<?php
180    require_once 'clean_table.inc';
181?>
182--EXPECTF--
183API: %d, SQL: %d
184Dumping table contents before INSERT...SELECT experiments...
185id = %d, label = 'b'
186id = %d, label = 'a'
187id = %d, label = 'c'
188Dumping table contents after INSERT...SELECT...
189id = %d, label = 'b'
190id = %d, label = 'a'
191id = %d, label = 'c'
192id = %d, label = 'a%d'
193id = %d, label = 'c%d'
194id = 101, label = 'b%d'
195done!
196