- 
                Notifications
    You must be signed in to change notification settings 
- Fork 556
Avoid RangeError on integers larger than LONG_LONG #764
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
Avoid RangeError on integers larger than LONG_LONG #764
Conversation
        
          
                spec/mysql2/statement_spec.rb
              
                Outdated
          
        
      | int64_min = -(1 << 63) | ||
| result = stmt.execute(int64_max, int64_min) | ||
| expect(result.to_a).to eq(['max' => int64_max, 'min' => int64_min]) | ||
| @client.query 'DROP TABLE IF EXISTS mysql2_stmt_q' | 
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.
No need to drop the table since these tests aren't creating/querying from it.
| Thank you! Will this also work in the future merged Integer / FixNum / BigNum world of Ruby 2.4.0? | 
f47cb50    to
    1a0703c      
    Compare
  
    | Yeah,  | 
| Ruby 1.8.7 does have  You could call Bignum#<=> to get the same effect? /* elsewhere in the file */
static ID id_cmp;
id_cmp = rb_intern("<=>");
/**/
#ifdef HAVE_RB_BIG_CMP
  VALUE less_than_llong_min = rb_big_cmp(bignum, LL2NUM(LLONG_MIN));
#else
  VALUE less_than_llong_min = rb_funcall(bignum, id_cmp, 1, LL2NUM(LLONG_MIN));
#endif
  if (less_than_llong_min == INT2FIX(-1)) {
    // code
  } else {
    // code
  } | 
| 
 I noticed  | 
        
          
                ext/mysql2/statement.c
              
                Outdated
          
        
      | #endif | ||
| if (len > 8) goto overflow; | ||
| if (RBIGNUM_POSITIVE_P(bignum)) { | ||
| num = rb_big2ull(bignum); | 
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.
Looks like if BIGSIZE(bignum) > SIZEOF_LONG_LONG, should be goto overflow.
http://rxr.whitequark.org/mri/source/bignum.c#3587
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.
Oh, enough to if (len > 8) goto overflow;?
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.
Yes. len > 8 means "Bignum's raw size is larger than 8 byte.
It is the same meaning with BIGSIZE(bignum) > SIZEOF_LONG_LONG.
Anyway the spec should have int64_max3 = 1 << 64; I'll add it tomorrow.
        
          
                ext/mysql2/statement.c
              
                Outdated
          
        
      | #else | ||
| len = RBIGNUM_LEN(bignum) * SIZEOF_BDIGITS; | ||
| #endif | ||
| if (len > 8) goto overflow; | 
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.
Length 8 is intended to be the same as sizeof(long long int) right? Would it make sense for the integer size to be passed in as an argument?
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.
fixed!
37410c4    to
    81fb72b      
    Compare
  
    | @sodabrew I reflected your comments. What do you think about current one? | 
| Sorry for the long delay on this PR, and it's run into a merge conflict since the recent  | 
CRuby's rb_big2ull raises RangeError if the argument is out of [LONG_MIN, ULONG_MAX]. To avoid that calling to_s and set as decimal with some tricks for edge cases. For Ruby 1.8.7, use rb_funcall() to call Bignum#<=> through Ruby space. For Ruby 1.9.3 and 2.0.0, use rb_big_cmp() to call Bignum#<=> directly. For Ruby 2.1 and above, use rb_absint_size() and I noticed len == 8 && nlz_bits == 0 && rb_absint_singlebit_p(bignum) is the best condition because it doesn't allocate extra object.
c2c67e7    to
    f59eb53      
    Compare
  
    | Hmm, rebased but it fails. I'll investigate later... | 
| Thanks! There's an interaction with #783 that results in the values getting cast as BigDecimal instead of a string on return. The reason is that MySQL defaults to returning numeric values as decimals unless it knows a specific integer size from a column definition or a procedure signature. | 
| I think #783 actually correctly fixed the big number types, and you can remove the   | 
* Use rb_absint_size and rb_absint_singlebit_p on Ruby 2.1 or later.
  They don't allocate new objects and fast.
* Use rb_big_cmp on Ruby 1.9.x and 2.0.0
* Use rb_funcall(bignum, rb_intern("<=>")) on Ruby 1.8 and REE
Note that this works on Ruby 2.4.
    f59eb53    to
    f10a337      
    Compare
  
    | @sodabrew Fixed as you says removed  | 
CRuby's rb_big2ull raises RangeError if the argument is out of [LONG_MIN, ULONG_MAX].
To avoid that calling to_s and set as decimal with some tricks for edge cases.