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.

Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +74 -0
  4. data/Rakefile +18 -0
  5. data/ext/trilogy-ruby/cast.c +272 -0
  6. data/ext/trilogy-ruby/cext.c +933 -0
  7. data/ext/trilogy-ruby/extconf.rb +16 -0
  8. data/ext/trilogy-ruby/inc/trilogy/blocking.h +163 -0
  9. data/ext/trilogy-ruby/inc/trilogy/buffer.h +64 -0
  10. data/ext/trilogy-ruby/inc/trilogy/builder.h +161 -0
  11. data/ext/trilogy-ruby/inc/trilogy/charset.h +277 -0
  12. data/ext/trilogy-ruby/inc/trilogy/client.h +546 -0
  13. data/ext/trilogy-ruby/inc/trilogy/error.h +43 -0
  14. data/ext/trilogy-ruby/inc/trilogy/packet_parser.h +34 -0
  15. data/ext/trilogy-ruby/inc/trilogy/protocol.h +756 -0
  16. data/ext/trilogy-ruby/inc/trilogy/reader.h +212 -0
  17. data/ext/trilogy-ruby/inc/trilogy/socket.h +111 -0
  18. data/ext/trilogy-ruby/inc/trilogy/vendor/curl_hostcheck.h +29 -0
  19. data/ext/trilogy-ruby/inc/trilogy/vendor/openssl_hostname_validation.h +51 -0
  20. data/ext/trilogy-ruby/inc/trilogy.h +8 -0
  21. data/ext/trilogy-ruby/src/blocking.c +241 -0
  22. data/ext/trilogy-ruby/src/buffer.c +60 -0
  23. data/ext/trilogy-ruby/src/builder.c +198 -0
  24. data/ext/trilogy-ruby/src/charset.c +212 -0
  25. data/ext/trilogy-ruby/src/client.c +728 -0
  26. data/ext/trilogy-ruby/src/error.c +17 -0
  27. data/ext/trilogy-ruby/src/packet_parser.c +140 -0
  28. data/ext/trilogy-ruby/src/protocol.c +676 -0
  29. data/ext/trilogy-ruby/src/reader.c +244 -0
  30. data/ext/trilogy-ruby/src/socket.c +623 -0
  31. data/ext/trilogy-ruby/src/vendor/curl_hostcheck.c +206 -0
  32. data/ext/trilogy-ruby/src/vendor/openssl_hostname_validation.c +175 -0
  33. data/ext/trilogy-ruby/trilogy-ruby.h +36 -0
  34. data/lib/trilogy/version.rb +3 -0
  35. data/lib/trilogy.rb +61 -0
  36. data/trilogy.gemspec +27 -0
  37. 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
+ }