Skip to content

Commit 01548f4

Browse files
committed
Avoid RangeError on integers larger than LONG_LONG
1 parent 6924e76 commit 01548f4

File tree

3 files changed

+48
-1
lines changed

3 files changed

+48
-1
lines changed

ext/mysql2/extconf.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ def asplode(lib)
1212
end
1313
end
1414

15+
# 2.1+
16+
have_func('rb_absint_size')
17+
1518
# 2.0-only
1619
have_header('ruby/thread.h') && have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
1720

ext/mysql2/statement.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,32 @@ static void set_buffer_for_string(MYSQL_BIND* bind_buffer, unsigned long *length
204204
xfree(length_buffers); \
205205
}
206206

207+
/* return 0 if the given bignum can cast as LONG_LONG, otherwise 1 */
208+
int my_big2ll(VALUE bignum, LONG_LONG *ptr)
209+
{
210+
unsigned LONG_LONG num;
211+
#ifdef HAVE_RB_ABSINT_SIZE
212+
if (rb_absint_size(bignum, NULL) > 8) goto overflow;
213+
#else
214+
if (RBIGNUM_LEN(bignum)*SIZEOF_BDIGITS > 8) goto overflow;
215+
#endif
216+
if (RBIGNUM_POSITIVE_P(bignum)) {
217+
num = rb_big2ull(bignum);
218+
if (num > LLONG_MAX)
219+
goto overflow;
220+
*ptr = num;
221+
}
222+
else {
223+
if (rb_big_cmp(bignum, LL2NUM(LLONG_MIN)) == INT2FIX(-1))
224+
goto overflow;
225+
*ptr = rb_big2ll(bignum);
226+
}
227+
return 0;
228+
overflow:
229+
*ptr = -1; /* TODO */
230+
return 1;
231+
}
232+
207233
/* call-seq: stmt.execute
208234
*
209235
* Executes the current prepared statement, returns +result+.
@@ -267,7 +293,7 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
267293
case T_BIGNUM:
268294
bind_buffers[i].buffer_type = MYSQL_TYPE_LONGLONG;
269295
bind_buffers[i].buffer = xmalloc(sizeof(long long int));
270-
*(LONG_LONG*)(bind_buffers[i].buffer) = rb_big2ll(argv[i]);
296+
my_big2ll(argv[i], (LONG_LONG*)(bind_buffers[i].buffer));
271297
break;
272298
case T_FLOAT:
273299
bind_buffers[i].buffer_type = MYSQL_TYPE_DOUBLE;

spec/mysql2/statement_spec.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,24 @@
5959
expect(rows).to eq([{ "1" => 1 }])
6060
end
6161

62+
it "should handle bignum but in int64_t" do
63+
stmt = @client.prepare('SELECT ? AS max, ? AS min')
64+
int64_max = (1 << 63)-1
65+
int64_min = -(1 << 63)
66+
result = stmt.execute(int64_max, int64_min)
67+
expect(result.to_a).to eq(['max' => int64_max, 'min' => int64_min])
68+
@client.query 'DROP TABLE IF EXISTS mysql2_stmt_q'
69+
end
70+
71+
it "should handle bignum but beyond int64_t" do
72+
stmt = @client.prepare('SELECT ? AS max1, ? AS min1')
73+
int64_max1 = (1 << 63)
74+
int64_min1 = -(1 << 63)-2
75+
result = stmt.execute(int64_max1, int64_min1)
76+
expect(result.to_a).to eq(['max1' => -1, 'min1' => -1])
77+
@client.query 'DROP TABLE IF EXISTS mysql2_stmt_q'
78+
end
79+
6280
it "should keep its result after other query" do
6381
@client.query 'USE test'
6482
@client.query 'CREATE TABLE IF NOT EXISTS mysql2_stmt_q(a int)'

0 commit comments

Comments
 (0)