trilogy 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of trilogy might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +74 -0
- data/Rakefile +18 -0
- data/ext/trilogy-ruby/cast.c +272 -0
- data/ext/trilogy-ruby/cext.c +933 -0
- data/ext/trilogy-ruby/extconf.rb +16 -0
- data/ext/trilogy-ruby/inc/trilogy/blocking.h +163 -0
- data/ext/trilogy-ruby/inc/trilogy/buffer.h +64 -0
- data/ext/trilogy-ruby/inc/trilogy/builder.h +161 -0
- data/ext/trilogy-ruby/inc/trilogy/charset.h +277 -0
- data/ext/trilogy-ruby/inc/trilogy/client.h +546 -0
- data/ext/trilogy-ruby/inc/trilogy/error.h +43 -0
- data/ext/trilogy-ruby/inc/trilogy/packet_parser.h +34 -0
- data/ext/trilogy-ruby/inc/trilogy/protocol.h +756 -0
- data/ext/trilogy-ruby/inc/trilogy/reader.h +212 -0
- data/ext/trilogy-ruby/inc/trilogy/socket.h +111 -0
- data/ext/trilogy-ruby/inc/trilogy/vendor/curl_hostcheck.h +29 -0
- data/ext/trilogy-ruby/inc/trilogy/vendor/openssl_hostname_validation.h +51 -0
- data/ext/trilogy-ruby/inc/trilogy.h +8 -0
- data/ext/trilogy-ruby/src/blocking.c +241 -0
- data/ext/trilogy-ruby/src/buffer.c +60 -0
- data/ext/trilogy-ruby/src/builder.c +198 -0
- data/ext/trilogy-ruby/src/charset.c +212 -0
- data/ext/trilogy-ruby/src/client.c +728 -0
- data/ext/trilogy-ruby/src/error.c +17 -0
- data/ext/trilogy-ruby/src/packet_parser.c +140 -0
- data/ext/trilogy-ruby/src/protocol.c +676 -0
- data/ext/trilogy-ruby/src/reader.c +244 -0
- data/ext/trilogy-ruby/src/socket.c +623 -0
- data/ext/trilogy-ruby/src/vendor/curl_hostcheck.c +206 -0
- data/ext/trilogy-ruby/src/vendor/openssl_hostname_validation.c +175 -0
- data/ext/trilogy-ruby/trilogy-ruby.h +36 -0
- data/lib/trilogy/version.rb +3 -0
- data/lib/trilogy.rb +61 -0
- data/trilogy.gemspec +27 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: aca8390c35be7813f34f018fab70db8630dbec8e1b9fa3962e5b432203ae0299
|
4
|
+
data.tar.gz: ac509a03ca6f611eb87603ba55749547fe8eaad0792107dd2bf855646726fb4c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2d66ea74cadbc2849a53d240b2a4b0a7094a31e9b79be12b92d1691c1aede8929d6752afeed46a5e2ad0666cc7f2e1aef170c55e8ec3b2caa91f5c0be369572c
|
7
|
+
data.tar.gz: b96064098c800476df66ad24eca177f3651b37679d8d634eb7301c4c97ca653e59da1f6acf3ea5e72af892fb0a8abea5d0f83378319bb73f81307cfac1fa08ac
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012-2021 GitHub, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# trilogy
|
2
|
+
|
3
|
+
Ruby bindings to the Trilogy client library
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
``` ruby
|
10
|
+
gem 'trilogy'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
```
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
```
|
22
|
+
$ gem install trilogy
|
23
|
+
```
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
``` ruby
|
28
|
+
client = Trilogy.new(host: "127.0.0.1", port: 3306, username: "root", read_timeout: 2)
|
29
|
+
if client.ping
|
30
|
+
client.query_options[:database_timezone] = :utc
|
31
|
+
client.change_db "mydb"
|
32
|
+
|
33
|
+
result = client.query("SELECT id, created_at FROM users LIMIT 10")
|
34
|
+
result.each_hash do |user|
|
35
|
+
p user
|
36
|
+
end
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
## Building
|
41
|
+
You should use the rake commands to build/install/release the gem
|
42
|
+
For instance:
|
43
|
+
```shell
|
44
|
+
bundle exec rake build
|
45
|
+
```
|
46
|
+
|
47
|
+
## Contributing
|
48
|
+
|
49
|
+
The official Ruby bindings are inside of the canonical trilogy repository itself.
|
50
|
+
|
51
|
+
1. Fork it ( https://github.com/github/trilogy/fork )
|
52
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
53
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
54
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
55
|
+
5. Create a new Pull Request
|
56
|
+
|
57
|
+
## mysql2 gem compatibility
|
58
|
+
|
59
|
+
The trilogy API was heavily inspired by the mysql2 gem but has a few notable
|
60
|
+
differences:
|
61
|
+
|
62
|
+
* The `query_options` hash doesn't inherit from the connection options hash.
|
63
|
+
This means that options like turning on/of casting will need to be set before
|
64
|
+
a query and not passed in at connect time.
|
65
|
+
* For performance reasons there is no `application_timezone` query option. If
|
66
|
+
casting is enabled and your database timezone is different than what the
|
67
|
+
application is expecting you'll need to do the conversion yourself later.
|
68
|
+
* While we still tag strings with the encoding configured on the field they came
|
69
|
+
from - for performance reasons no automatic transcoding into
|
70
|
+
`Encoding.default_internal` is done. Similarly to not automatically converting
|
71
|
+
Time objects from `database_timezone` into `application_timezone`, we leave
|
72
|
+
the transcoding step up to the caller.
|
73
|
+
* There is no `as` query option. Calling `Trilogy::Result#each` will yield an array
|
74
|
+
of row values. If you want a hash you should use `Trilogy::Result#each_hash`.
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "bundler/gem_helper"
|
2
|
+
require "rake/extensiontask"
|
3
|
+
|
4
|
+
Rake::ExtensionTask.new do |ext|
|
5
|
+
ext.name = "cext"
|
6
|
+
ext.ext_dir = "ext/trilogy-ruby"
|
7
|
+
ext.lib_dir = "lib/trilogy"
|
8
|
+
end
|
9
|
+
|
10
|
+
task :test => :compile do
|
11
|
+
system('script/test')
|
12
|
+
end
|
13
|
+
|
14
|
+
task :default => :test
|
15
|
+
|
16
|
+
task :console => :compile do
|
17
|
+
sh "ruby -I lib -r trilogy -S irb"
|
18
|
+
end
|
@@ -0,0 +1,272 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <ruby/encoding.h>
|
3
|
+
|
4
|
+
#include <trilogy.h>
|
5
|
+
|
6
|
+
#include "trilogy-ruby.h"
|
7
|
+
|
8
|
+
#define CAST_STACK_SIZE 64
|
9
|
+
|
10
|
+
static ID id_BigDecimal, id_new, id_local, id_localtime, id_utc;
|
11
|
+
|
12
|
+
static const char *ruby_encoding_name_map[] = {
|
13
|
+
[TRILOGY_ENCODING_ARMSCII8] = NULL,
|
14
|
+
[TRILOGY_ENCODING_ASCII] = "US-ASCII",
|
15
|
+
[TRILOGY_ENCODING_BIG5] = "Big5",
|
16
|
+
[TRILOGY_ENCODING_BINARY] = "BINARY",
|
17
|
+
[TRILOGY_ENCODING_CP1250] = "Windows-1250",
|
18
|
+
[TRILOGY_ENCODING_CP1251] = "Windows-1251",
|
19
|
+
[TRILOGY_ENCODING_CP1256] = "Windows-1256",
|
20
|
+
[TRILOGY_ENCODING_CP1257] = "Windows-1257",
|
21
|
+
[TRILOGY_ENCODING_CP850] = "CP850",
|
22
|
+
[TRILOGY_ENCODING_CP852] = "CP852",
|
23
|
+
[TRILOGY_ENCODING_CP866] = "IBM866",
|
24
|
+
[TRILOGY_ENCODING_CP932] = "Windows-31J",
|
25
|
+
[TRILOGY_ENCODING_DEC8] = NULL,
|
26
|
+
[TRILOGY_ENCODING_EUCJPMS] = "eucJP-ms",
|
27
|
+
[TRILOGY_ENCODING_EUCKR] = "EUC-KR",
|
28
|
+
[TRILOGY_ENCODING_GB2312] = "GB2312",
|
29
|
+
[TRILOGY_ENCODING_GBK] = "GBK",
|
30
|
+
[TRILOGY_ENCODING_GEOSTD8] = NULL,
|
31
|
+
[TRILOGY_ENCODING_GREEK] = "ISO-8859-7",
|
32
|
+
[TRILOGY_ENCODING_HEBREW] = "ISO-8859-8",
|
33
|
+
[TRILOGY_ENCODING_HP8] = NULL,
|
34
|
+
[TRILOGY_ENCODING_KEYBCS2] = NULL,
|
35
|
+
[TRILOGY_ENCODING_KOI8R] = "KOI8-R",
|
36
|
+
[TRILOGY_ENCODING_KOI8U] = "KOI8-U",
|
37
|
+
[TRILOGY_ENCODING_LATIN1] = "ISO-8859-1",
|
38
|
+
[TRILOGY_ENCODING_LATIN2] = "ISO-8859-2",
|
39
|
+
[TRILOGY_ENCODING_LATIN5] = "ISO-8859-9",
|
40
|
+
[TRILOGY_ENCODING_LATIN7] = "ISO-8859-13",
|
41
|
+
[TRILOGY_ENCODING_MACCE] = "macCentEuro",
|
42
|
+
[TRILOGY_ENCODING_MACROMAN] = "macRoman",
|
43
|
+
[TRILOGY_ENCODING_NONE] = NULL,
|
44
|
+
[TRILOGY_ENCODING_SJIS] = "Shift_JIS",
|
45
|
+
[TRILOGY_ENCODING_SWE7] = NULL,
|
46
|
+
[TRILOGY_ENCODING_TIS620] = "TIS-620",
|
47
|
+
[TRILOGY_ENCODING_UCS2] = "UTF-16BE",
|
48
|
+
[TRILOGY_ENCODING_UJIS] = "eucJP-ms",
|
49
|
+
[TRILOGY_ENCODING_UTF16] = "UTF-16BE",
|
50
|
+
[TRILOGY_ENCODING_UTF32] = "UTF-32",
|
51
|
+
[TRILOGY_ENCODING_UTF8] = "UTF-8",
|
52
|
+
[TRILOGY_ENCODING_UTF8MB4] = "UTF-8",
|
53
|
+
|
54
|
+
[TRILOGY_ENCODING_MAX] = NULL,
|
55
|
+
};
|
56
|
+
|
57
|
+
static int encoding_for_charset(TRILOGY_CHARSET_t charset)
|
58
|
+
{
|
59
|
+
static int map[TRILOGY_CHARSET_MAX];
|
60
|
+
|
61
|
+
if (map[charset]) {
|
62
|
+
return map[charset];
|
63
|
+
}
|
64
|
+
|
65
|
+
const char *encoding_name = ruby_encoding_name_map[trilogy_encoding_from_charset(charset)];
|
66
|
+
|
67
|
+
return map[charset] = (encoding_name ? rb_enc_find_index(encoding_name) : -1);
|
68
|
+
}
|
69
|
+
|
70
|
+
static void cstr_from_value(char *buf, const trilogy_value_t *value, const char *errmsg)
|
71
|
+
{
|
72
|
+
|
73
|
+
if (value->data_len > CAST_STACK_SIZE - 1) {
|
74
|
+
rb_raise(rb_cTrilogyError, errmsg, (int)value->data_len, (char *)value->data);
|
75
|
+
}
|
76
|
+
|
77
|
+
memcpy(buf, value->data, value->data_len);
|
78
|
+
buf[value->data_len] = 0;
|
79
|
+
}
|
80
|
+
|
81
|
+
static unsigned long long ull_from_buf(const char *digits, size_t len)
|
82
|
+
{
|
83
|
+
if (!len)
|
84
|
+
return 0;
|
85
|
+
|
86
|
+
unsigned long long val = 0;
|
87
|
+
|
88
|
+
while (len--) {
|
89
|
+
unsigned digit = *digits++ - '0';
|
90
|
+
val = val * 10 + digit;
|
91
|
+
}
|
92
|
+
|
93
|
+
return val;
|
94
|
+
}
|
95
|
+
|
96
|
+
static long long ll_from_buf(const char *digits, size_t len)
|
97
|
+
{
|
98
|
+
if (!len)
|
99
|
+
return 0;
|
100
|
+
|
101
|
+
if (digits[0] == '-') {
|
102
|
+
return -(long long)ull_from_buf(&digits[1], len - 1);
|
103
|
+
} else {
|
104
|
+
return (long long)ull_from_buf(digits, len);
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
VALUE
|
109
|
+
rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *column,
|
110
|
+
const struct rb_trilogy_cast_options *options)
|
111
|
+
{
|
112
|
+
if (value->is_null) {
|
113
|
+
return Qnil;
|
114
|
+
}
|
115
|
+
|
116
|
+
if (options->cast) {
|
117
|
+
switch (column->type) {
|
118
|
+
case TRILOGY_TYPE_BIT: {
|
119
|
+
if (options->cast_booleans && column->len == 1) {
|
120
|
+
return *(const char *)value->data == 1 ? Qtrue : Qfalse;
|
121
|
+
}
|
122
|
+
break;
|
123
|
+
}
|
124
|
+
case TRILOGY_TYPE_TINY: {
|
125
|
+
if (options->cast_booleans && column->len == 1) {
|
126
|
+
return *(const char *)value->data != '0' ? Qtrue : Qfalse;
|
127
|
+
}
|
128
|
+
/* fall through */
|
129
|
+
}
|
130
|
+
case TRILOGY_TYPE_SHORT:
|
131
|
+
case TRILOGY_TYPE_LONG:
|
132
|
+
case TRILOGY_TYPE_LONGLONG:
|
133
|
+
case TRILOGY_TYPE_INT24:
|
134
|
+
case TRILOGY_TYPE_YEAR: {
|
135
|
+
if (column->flags & TRILOGY_COLUMN_FLAG_UNSIGNED) {
|
136
|
+
unsigned long long num = ull_from_buf(value->data, value->data_len);
|
137
|
+
return ULL2NUM(num);
|
138
|
+
} else {
|
139
|
+
long long num = ll_from_buf(value->data, value->data_len);
|
140
|
+
return LL2NUM(num);
|
141
|
+
}
|
142
|
+
}
|
143
|
+
case TRILOGY_TYPE_DECIMAL:
|
144
|
+
case TRILOGY_TYPE_NEWDECIMAL: {
|
145
|
+
// TODO - optimize so we don't have to allocate a ruby string for
|
146
|
+
// decimal columns
|
147
|
+
VALUE str = rb_str_new(value->data, value->data_len);
|
148
|
+
return rb_funcall(rb_mKernel, id_BigDecimal, 1, str);
|
149
|
+
}
|
150
|
+
case TRILOGY_TYPE_FLOAT:
|
151
|
+
case TRILOGY_TYPE_DOUBLE: {
|
152
|
+
char cstr[CAST_STACK_SIZE];
|
153
|
+
cstr_from_value(cstr, value, "Invalid double value: %.*s");
|
154
|
+
|
155
|
+
char *err;
|
156
|
+
double dbl = strtod(cstr, &err);
|
157
|
+
|
158
|
+
if (*err != 0) {
|
159
|
+
rb_raise(rb_cTrilogyError, "Invalid double value: %.*s", (int)value->data_len, (char *)value->data);
|
160
|
+
}
|
161
|
+
return rb_float_new(dbl);
|
162
|
+
}
|
163
|
+
case TRILOGY_TYPE_TIMESTAMP:
|
164
|
+
case TRILOGY_TYPE_DATETIME: {
|
165
|
+
int year, month, day, hour, min, sec;
|
166
|
+
char msec_char[7] = {0};
|
167
|
+
|
168
|
+
char cstr[CAST_STACK_SIZE];
|
169
|
+
cstr_from_value(cstr, value, "Invalid date: %.*s");
|
170
|
+
|
171
|
+
int tokens = sscanf(cstr, "%4u-%2u-%2u %2u:%2u:%2u.%6s", &year, &month, &day, &hour, &min, &sec, msec_char);
|
172
|
+
|
173
|
+
// msec might not be present, so check for 6 tokens rather than 7
|
174
|
+
if (tokens < 6) {
|
175
|
+
return Qnil;
|
176
|
+
}
|
177
|
+
|
178
|
+
if (year == 0 && month == 0 && day == 0 && hour == 0 && min == 0 && sec == 0) {
|
179
|
+
return Qnil;
|
180
|
+
}
|
181
|
+
|
182
|
+
if (month < 1 || day < 1) {
|
183
|
+
rb_raise(rb_cTrilogyError, "Invalid date: %.*s", (int)value->data_len, (char *)value->data);
|
184
|
+
}
|
185
|
+
|
186
|
+
// pad out msec_char with zeroes at the end as it could be at any
|
187
|
+
// level of precision
|
188
|
+
for (size_t i = strlen(msec_char); i < sizeof(msec_char) - 1; i++) {
|
189
|
+
msec_char[i] = '0';
|
190
|
+
}
|
191
|
+
|
192
|
+
return rb_funcall(rb_cTime, options->database_local_time ? id_local : id_utc, 7, INT2NUM(year),
|
193
|
+
INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec),
|
194
|
+
INT2NUM(atoi(msec_char)));
|
195
|
+
}
|
196
|
+
case TRILOGY_TYPE_DATE: {
|
197
|
+
int year, month, day;
|
198
|
+
|
199
|
+
char cstr[CAST_STACK_SIZE];
|
200
|
+
cstr_from_value(cstr, value, "Invalid date: %.*s");
|
201
|
+
|
202
|
+
int tokens = sscanf(cstr, "%4u-%2u-%2u", &year, &month, &day);
|
203
|
+
VALUE Date = rb_const_get(rb_cObject, rb_intern("Date"));
|
204
|
+
|
205
|
+
if (tokens < 3) {
|
206
|
+
return Qnil;
|
207
|
+
}
|
208
|
+
|
209
|
+
if (year == 0 && month == 0 && day == 0) {
|
210
|
+
return Qnil;
|
211
|
+
}
|
212
|
+
|
213
|
+
if (month < 1 || day < 1) {
|
214
|
+
rb_raise(rb_cTrilogyError, "Invalid date: %.*s", (int)value->data_len, (char *)value->data);
|
215
|
+
}
|
216
|
+
|
217
|
+
return rb_funcall(Date, id_new, 3, INT2NUM(year), INT2NUM(month), INT2NUM(day));
|
218
|
+
}
|
219
|
+
case TRILOGY_TYPE_TIME: {
|
220
|
+
int hour, min, sec;
|
221
|
+
char msec_char[7] = {0};
|
222
|
+
|
223
|
+
char cstr[CAST_STACK_SIZE];
|
224
|
+
cstr_from_value(cstr, value, "Invalid time: %.*s");
|
225
|
+
|
226
|
+
int tokens = sscanf(cstr, "%2u:%2u:%2u.%6s", &hour, &min, &sec, msec_char);
|
227
|
+
|
228
|
+
if (tokens < 3) {
|
229
|
+
return Qnil;
|
230
|
+
}
|
231
|
+
|
232
|
+
if (hour == 0 && min == 0 && sec == 0) {
|
233
|
+
return Qnil;
|
234
|
+
}
|
235
|
+
|
236
|
+
// pad out msec_char with zeroes at the end as it could be at any
|
237
|
+
// level of precision
|
238
|
+
for (size_t i = strlen(msec_char); i < sizeof(msec_char) - 1; i++) {
|
239
|
+
msec_char[i] = 0;
|
240
|
+
}
|
241
|
+
|
242
|
+
return rb_funcall(rb_cTime, options->database_local_time ? id_local : id_utc, 7, INT2NUM(2000), INT2NUM(1),
|
243
|
+
INT2NUM(1), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), INT2NUM(atoi(msec_char)));
|
244
|
+
}
|
245
|
+
default:
|
246
|
+
break;
|
247
|
+
}
|
248
|
+
}
|
249
|
+
|
250
|
+
// for all other types, just return a string
|
251
|
+
|
252
|
+
VALUE str = rb_str_new(value->data, value->data_len);
|
253
|
+
|
254
|
+
int encoding_index = encoding_for_charset(column->charset);
|
255
|
+
if (encoding_index != -1) {
|
256
|
+
rb_enc_associate_index(str, encoding_index);
|
257
|
+
}
|
258
|
+
|
259
|
+
return str;
|
260
|
+
}
|
261
|
+
|
262
|
+
void rb_trilogy_cast_init(void)
|
263
|
+
{
|
264
|
+
rb_require("bigdecimal");
|
265
|
+
rb_require("date");
|
266
|
+
|
267
|
+
id_BigDecimal = rb_intern("BigDecimal");
|
268
|
+
id_new = rb_intern("new");
|
269
|
+
id_local = rb_intern("local");
|
270
|
+
id_localtime = rb_intern("localtime");
|
271
|
+
id_utc = rb_intern("utc");
|
272
|
+
}
|