do_sqlite3 0.10.3-x86-mswin32-60 → 0.10.4.rc1-x86-mswin32-60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,170 @@
1
+ #ifndef _DO_COMMON_H_
2
+ #define _DO_COMMON_H_
3
+
4
+ #include <ruby.h>
5
+
6
+ #ifdef _WIN32
7
+ #define cCommand_execute cCommand_execute_sync
8
+ typedef signed __int64 do_int64;
9
+ #else
10
+ #define cCommand_execute cCommand_execute_async
11
+ typedef signed long long int do_int64;
12
+ #endif
13
+
14
+ #ifdef HAVE_RUBY_ENCODING_H
15
+ #include <ruby/encoding.h>
16
+
17
+ #define DO_STR_NEW2(str, encoding, internal_encoding) \
18
+ ({ \
19
+ VALUE _string = rb_str_new2((const char *)str); \
20
+ if(encoding != -1) { \
21
+ rb_enc_associate_index(_string, encoding); \
22
+ } \
23
+ if(internal_encoding) { \
24
+ _string = rb_str_export_to_enc(_string, internal_encoding); \
25
+ } \
26
+ _string; \
27
+ })
28
+
29
+ #define DO_STR_NEW(str, len, encoding, internal_encoding) \
30
+ ({ \
31
+ VALUE _string = rb_str_new((const char *)str, (long)len); \
32
+ if(encoding != -1) { \
33
+ rb_enc_associate_index(_string, encoding); \
34
+ } \
35
+ if(internal_encoding) { \
36
+ _string = rb_str_export_to_enc(_string, internal_encoding); \
37
+ } \
38
+ _string; \
39
+ })
40
+
41
+ # else
42
+
43
+ #define DO_STR_NEW2(str, encoding, internal_encoding) \
44
+ rb_str_new2((const char *)str)
45
+
46
+ #define DO_STR_NEW(str, len, encoding, internal_encoding) \
47
+ rb_str_new((const char *)str, (long)len)
48
+ #endif
49
+
50
+ // Needed for defining error.h
51
+ struct errcodes {
52
+ int error_no;
53
+ const char *error_name;
54
+ const char *exception;
55
+ };
56
+
57
+ #define ERRCODE(name,message) {name, #name, message}
58
+
59
+ // To store rb_intern values
60
+ extern ID ID_NEW;
61
+ extern ID ID_NEW_DATE;
62
+ extern ID ID_CONST_GET;
63
+ extern ID ID_RATIONAL;
64
+ extern ID ID_ESCAPE;
65
+ extern ID ID_STRFTIME;
66
+ extern ID ID_LOG;
67
+
68
+ // Reference to Extlib module
69
+ extern VALUE mExtlib;
70
+ extern VALUE rb_cByteArray;
71
+
72
+ // References to DataObjects base classes
73
+ extern VALUE mDO;
74
+ extern VALUE mEncoding;
75
+ extern VALUE cDO_Quoting;
76
+ extern VALUE cDO_Connection;
77
+ extern VALUE cDO_Command;
78
+ extern VALUE cDO_Result;
79
+ extern VALUE cDO_Reader;
80
+ extern VALUE cDO_Logger;
81
+ extern VALUE cDO_Logger_Message;
82
+ extern VALUE cDO_Extension;
83
+ extern VALUE eConnectionError;
84
+ extern VALUE eDataError;
85
+
86
+ // References to Ruby classes that we'll need
87
+ extern VALUE rb_cDate;
88
+ extern VALUE rb_cDateTime;
89
+ extern VALUE rb_cBigDecimal;
90
+
91
+ extern void data_objects_debug(VALUE connection, VALUE string, struct timeval *start);
92
+ extern char *get_uri_option(VALUE query_hash, const char *key);
93
+ extern void assert_file_exists(char *file, const char *message);
94
+ extern VALUE build_query_from_args(VALUE klass, int count, VALUE *args);
95
+
96
+ extern void reduce(do_int64 *numerator, do_int64 *denominator);
97
+ extern int jd_from_date(int year, int month, int day);
98
+ extern VALUE seconds_to_offset(long seconds_offset);
99
+ extern VALUE timezone_to_offset(int hour_offset, int minute_offset);
100
+
101
+ extern VALUE parse_date(const char *date);
102
+ extern VALUE parse_time(const char *date);
103
+ extern VALUE parse_date_time(const char *date);
104
+
105
+ extern VALUE cConnection_character_set(VALUE self);
106
+ extern VALUE cConnection_is_using_socket(VALUE self);
107
+ extern VALUE cConnection_ssl_cipher(VALUE self);
108
+ extern VALUE cConnection_quote_time(VALUE self, VALUE value);
109
+ extern VALUE cConnection_quote_date_time(VALUE self, VALUE value);
110
+ extern VALUE cConnection_quote_date(VALUE self, VALUE value);
111
+
112
+ extern VALUE cCommand_set_types(int argc, VALUE *argv, VALUE self);
113
+
114
+ extern VALUE cReader_values(VALUE self);
115
+ extern VALUE cReader_fields(VALUE self);
116
+ extern VALUE cReader_field_count(VALUE self);
117
+
118
+ extern void common_init(void);
119
+
120
+ static inline VALUE do_const_get(VALUE scope, const char *constant) {
121
+ return rb_funcall(scope, ID_CONST_GET, 1, rb_str_new2(constant));
122
+ }
123
+
124
+ static inline VALUE do_str_new(const void *string, long length, int encoding, void *internal_encoding) {
125
+ VALUE new_string = rb_str_new(string, length);
126
+
127
+ #ifdef HAVE_RUBY_ENCODING_H
128
+ if(encoding != -1) {
129
+ rb_enc_associate_index(new_string, encoding);
130
+ }
131
+
132
+ if(internal_encoding) {
133
+ new_string = rb_str_export_to_enc(new_string, internal_encoding);
134
+ }
135
+ #endif
136
+
137
+ return new_string;
138
+ }
139
+
140
+ static inline VALUE do_str_new2(const void *string, int encoding, void *internal_encoding) {
141
+ VALUE new_string = rb_str_new2(string);
142
+
143
+ #ifdef HAVE_RUBY_ENCODING_H
144
+ if(encoding != -1) {
145
+ rb_enc_associate_index(new_string, encoding);
146
+ }
147
+
148
+ if(internal_encoding) {
149
+ new_string = rb_str_export_to_enc(new_string, internal_encoding);
150
+ }
151
+ #endif
152
+
153
+ return new_string;
154
+ }
155
+
156
+ static inline void do_define_errors(VALUE scope, const struct errcodes *errors) {
157
+ const struct errcodes *e;
158
+
159
+ for (e = errors; e->error_name; e++) {
160
+ rb_const_set(scope, rb_intern(e->error_name), INT2NUM(e->error_no));
161
+ }
162
+ }
163
+
164
+ extern void do_raise_error(VALUE self, const struct errcodes *errors, int errnum, const char *message, VALUE query, VALUE state);
165
+
166
+ extern VALUE do_typecast(const char *value, long length, const VALUE type, int encoding);
167
+
168
+ #define RSTRING_NOT_MODIFIED
169
+
170
+ #endif
@@ -1,343 +1,96 @@
1
1
  #include "do_sqlite3.h"
2
2
  #include "error.h"
3
- // To store rb_intern values
4
-
5
- static ID ID_NEW;
6
- static ID ID_NEW_DATE;
7
- static ID ID_CONST_GET;
8
- static ID ID_RATIONAL;
9
- static ID ID_ESCAPE;
10
- static ID ID_LOG;
11
-
12
- static VALUE mExtlib;
13
-
14
- static VALUE mDO;
15
- static VALUE cDO_Quoting;
16
- static VALUE cDO_Connection;
17
- static VALUE cDO_Command;
18
- static VALUE cDO_Result;
19
- static VALUE cDO_Reader;
20
- static VALUE cDO_Logger;
21
- static VALUE cDO_Logger_Message;
22
-
23
- static VALUE rb_cDate;
24
- static VALUE rb_cDateTime;
25
- static VALUE rb_cBigDecimal;
26
- static VALUE rb_cByteArray;
27
-
28
- static VALUE mSqlite3;
29
- static VALUE cConnection;
30
- static VALUE cCommand;
31
- static VALUE cResult;
32
- static VALUE cReader;
33
- static VALUE eConnectionError;
34
- static VALUE eDataError;
35
-
36
- static VALUE OPEN_FLAG_READONLY;
37
- static VALUE OPEN_FLAG_READWRITE;
38
- static VALUE OPEN_FLAG_CREATE;
39
- static VALUE OPEN_FLAG_NO_MUTEX;
40
- static VALUE OPEN_FLAG_FULL_MUTEX;
41
-
42
- // Find the greatest common denominator and reduce the provided numerator and denominator.
43
- // This replaces calles to Rational.reduce! which does the same thing, but really slowly.
44
- static void reduce( do_int64 *numerator, do_int64 *denominator ) {
45
- do_int64 a, b, c = 0;
46
- a = *numerator;
47
- b = *denominator;
48
- while ( a != 0 ) {
49
- c = a; a = b % a; b = c;
50
- }
51
- *numerator = *numerator / b;
52
- *denominator = *denominator / b;
53
- }
54
-
55
- // Generate the date integer which Date.civil_to_jd returns
56
- static int jd_from_date(int year, int month, int day) {
57
- int a, b;
58
- if ( month <= 2 ) {
59
- year -= 1;
60
- month += 12;
61
- }
62
- a = year / 100;
63
- b = 2 - a + (a / 4);
64
- return (int) (floor(365.25 * (year + 4716)) + floor(30.6001 * (month + 1)) + day + b - 1524);
65
- }
66
-
67
- static void data_objects_debug(VALUE connection, VALUE string, struct timeval* start) {
68
- struct timeval stop;
69
- VALUE message;
70
3
 
71
- gettimeofday(&stop, NULL);
72
- do_int64 duration = (stop.tv_sec - start->tv_sec) * 1000000 + stop.tv_usec - start->tv_usec;
4
+ #include "do_common.h"
73
5
 
74
- message = rb_funcall(cDO_Logger_Message, ID_NEW, 3, string, rb_time_new(start->tv_sec, start->tv_usec), INT2NUM(duration));
6
+ VALUE mSqlite3;
7
+ VALUE cConnection;
8
+ VALUE cCommand;
9
+ VALUE cResult;
10
+ VALUE cReader;
75
11
 
76
- rb_funcall(connection, ID_LOG, 1, message);
77
- }
12
+ VALUE OPEN_FLAG_READONLY;
13
+ VALUE OPEN_FLAG_READWRITE;
14
+ VALUE OPEN_FLAG_CREATE;
15
+ VALUE OPEN_FLAG_NO_MUTEX;
16
+ VALUE OPEN_FLAG_FULL_MUTEX;
78
17
 
79
- static void raise_error(VALUE self, sqlite3 *result, VALUE query) {
80
- VALUE exception;
18
+ void raise_error(VALUE self, sqlite3 *result, VALUE query) {
19
+ int errnum = sqlite3_errcode(result);
81
20
  const char *message = sqlite3_errmsg(result);
82
- const char *exception_type = "SQLError";
83
- int sqlite3_errno = sqlite3_errcode(result);
84
-
85
- struct errcodes *errs;
86
-
87
- for (errs = errors; errs->error_name; errs++) {
88
- if(errs->error_no == sqlite3_errno) {
89
- exception_type = errs->exception;
90
- break;
91
- }
92
- }
93
-
94
-
95
- VALUE uri = rb_funcall(rb_iv_get(self, "@connection"), rb_intern("to_s"), 0);
96
-
97
- exception = rb_funcall(CONST_GET(mDO, exception_type), ID_NEW, 5,
98
- rb_str_new2(message),
99
- INT2NUM(sqlite3_errno),
100
- rb_str_new2(""),
101
- query,
102
- uri);
103
- rb_exc_raise(exception);
104
- }
105
-
106
- static VALUE parse_date(char *date) {
107
- int year, month, day;
108
- int jd, ajd;
109
- VALUE rational;
110
-
111
- sscanf(date, "%4d-%2d-%2d", &year, &month, &day);
112
-
113
- jd = jd_from_date(year, month, day);
114
-
115
- // Math from Date.jd_to_ajd
116
- ajd = jd * 2 - 1;
117
- rational = rb_funcall(rb_mKernel, ID_RATIONAL, 2, INT2NUM(ajd), INT2NUM(2));
118
- return rb_funcall(rb_cDate, ID_NEW_DATE, 3, rational, INT2NUM(0), INT2NUM(2299161));
119
- }
120
-
121
- // Creates a Rational for use as a Timezone offset to be passed to DateTime.new!
122
- static VALUE seconds_to_offset(do_int64 num) {
123
- do_int64 den = 86400;
124
- reduce(&num, &den);
125
- return rb_funcall(rb_mKernel, ID_RATIONAL, 2, rb_ll2inum(num), rb_ll2inum(den));
126
- }
127
-
128
- static VALUE timezone_to_offset(int hour_offset, int minute_offset) {
129
- do_int64 seconds = 0;
130
-
131
- seconds += hour_offset * 3600;
132
- seconds += minute_offset * 60;
21
+ VALUE sql_state = rb_str_new2("");
133
22
 
134
- return seconds_to_offset(seconds);
23
+ do_raise_error(self, errors, errnum, message, query, sql_state);
135
24
  }
136
25
 
137
- static VALUE parse_date_time(char *date) {
138
- VALUE ajd, offset;
139
-
140
- int year, month, day, hour, min, sec, usec, hour_offset, minute_offset;
141
- int jd;
142
- do_int64 num, den;
143
-
144
- long int gmt_offset;
145
- int dst_adjustment;
146
-
147
- time_t rawtime;
148
- struct tm timeinfo;
149
-
150
- int tokens_read, max_tokens;
26
+ VALUE typecast(sqlite3_stmt *stmt, int i, VALUE type, int encoding) {
27
+ int original_type = sqlite3_column_type(stmt, i);
28
+ int length = sqlite3_column_bytes(stmt, i);
151
29
 
152
- if ( strcmp(date, "") == 0 ) {
30
+ if (original_type == SQLITE_NULL) {
153
31
  return Qnil;
154
32
  }
155
33
 
156
- if (0 != strchr(date, '.')) {
157
- // This is a datetime with sub-second precision
158
- tokens_read = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d.%d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &usec, &hour_offset, &minute_offset);
159
- max_tokens = 9;
160
- } else {
161
- // This is a datetime second precision
162
- tokens_read = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &hour_offset, &minute_offset);
163
- max_tokens = 8;
164
- }
165
-
166
- if (max_tokens == tokens_read) {
167
- // We read the Date, Time, and Timezone info
168
- minute_offset *= hour_offset < 0 ? -1 : 1;
169
- } else if ((max_tokens - 1) == tokens_read) {
170
- // We read the Date and Time, but no Minute Offset
171
- minute_offset = 0;
172
- } else if (tokens_read == 3 || tokens_read >= (max_tokens - 3)) {
173
- if (tokens_read == 3) {
174
- hour = 0;
175
- min = 0;
176
- hour_offset = 0;
177
- minute_offset = 0;
178
- sec = 0;
179
- }
180
- // We read the Date and Time, default to the current locale's offset
181
-
182
- tzset();
183
-
184
- // Get localtime
185
- time(&rawtime);
186
- #ifdef HAVE_LOCALTIME_R
187
- localtime_r(&rawtime, &timeinfo);
188
- #else
189
- timeinfo = *localtime(&rawtime);
190
- #endif
191
-
192
- timeinfo.tm_sec = sec;
193
- timeinfo.tm_min = min;
194
- timeinfo.tm_hour = hour;
195
- timeinfo.tm_mday = day;
196
- timeinfo.tm_mon = month;
197
- timeinfo.tm_year = year - 1900;
198
- timeinfo.tm_isdst = -1;
199
-
200
- // Update tm_isdst
201
- mktime(&timeinfo);
202
-
203
- if (timeinfo.tm_isdst) {
204
- dst_adjustment = 3600;
205
- } else {
206
- dst_adjustment = 0;
207
- }
208
-
209
- // Reset to GM Time
210
- #ifdef HAVE_GMTIME_R
211
- gmtime_r(&rawtime, &timeinfo);
212
- #else
213
- timeinfo = *gmtime(&rawtime);
214
- #endif
215
-
216
- gmt_offset = rawtime - mktime(&timeinfo);
217
-
218
- if (dst_adjustment) {
219
- gmt_offset += dst_adjustment;
220
- }
221
-
222
- hour_offset = ((int)gmt_offset / 3600);
223
- minute_offset = ((int)gmt_offset % 3600 / 60);
224
-
225
- } else {
226
- // Something went terribly wrong
227
- rb_raise(eDataError, "Couldn't parse date: %s", date);
228
- }
229
-
230
- jd = jd_from_date(year, month, day);
231
-
232
- // Generate ajd with fractional days for the time
233
- // Extracted from Date#jd_to_ajd, Date#day_fraction_to_time, and Rational#+ and #-
234
- num = (hour * 1440) + (min * 24);
235
-
236
- // Modify the numerator so when we apply the timezone everything works out
237
- num -= (hour_offset * 1440) + (minute_offset * 24);
238
-
239
- den = (24 * 1440);
240
- reduce(&num, &den);
241
-
242
- num = (num * 86400) + (sec * den);
243
- den = den * 86400;
244
- reduce(&num, &den);
245
-
246
- num = (jd * den) + num;
247
-
248
- num = num * 2;
249
- num = num - den;
250
- den = den * 2;
251
-
252
- reduce(&num, &den);
253
-
254
- ajd = rb_funcall(rb_mKernel, ID_RATIONAL, 2, rb_ull2inum(num), rb_ull2inum(den));
255
- offset = timezone_to_offset(hour_offset, minute_offset);
256
-
257
- return rb_funcall(rb_cDateTime, ID_NEW_DATE, 3, ajd, offset, INT2NUM(2299161));
258
- }
259
-
260
- static VALUE parse_time(char *date) {
261
-
262
- int year, month, day, hour, min, sec, usec, tokens, hour_offset, minute_offset;
263
-
264
- if (0 != strchr(date, '.')) {
265
- // This is a datetime with sub-second precision
266
- tokens = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d.%d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &usec, &hour_offset, &minute_offset);
267
- } else {
268
- // This is a datetime second precision
269
- tokens = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &hour_offset, &minute_offset);
270
- usec = 0;
271
- if(tokens == 3) {
272
- hour = 0;
273
- min = 0;
274
- sec = 0;
275
- hour_offset = 0;
276
- minute_offset = 0;
277
- }
278
- }
279
-
280
- return rb_funcall(rb_cTime, rb_intern("local"), 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), INT2NUM(usec));
281
- }
282
-
283
- static VALUE typecast(sqlite3_stmt *stmt, int i, VALUE type, int encoding) {
284
- VALUE ruby_value = Qnil;
285
- int original_type = sqlite3_column_type(stmt, i);
286
- int length = sqlite3_column_bytes(stmt, i);
287
- if ( original_type == SQLITE_NULL ) {
288
- return ruby_value;
289
- }
290
-
291
34
  #ifdef HAVE_RUBY_ENCODING_H
292
- rb_encoding * internal_encoding = rb_default_internal_encoding();
35
+ rb_encoding *internal_encoding = rb_default_internal_encoding();
293
36
  #else
294
- void * internal_encoding = NULL;
37
+ void *internal_encoding = NULL;
295
38
  #endif
296
39
 
297
- if(type == Qnil) {
298
- switch(original_type) {
299
- case SQLITE_INTEGER: {
40
+ if (type == Qnil) {
41
+ switch (original_type) {
42
+ case SQLITE_INTEGER:
300
43
  type = rb_cInteger;
301
44
  break;
302
- }
303
- case SQLITE_FLOAT: {
45
+
46
+ case SQLITE_FLOAT:
304
47
  type = rb_cFloat;
305
48
  break;
306
- }
307
- case SQLITE_BLOB: {
49
+
50
+ case SQLITE_BLOB:
308
51
  type = rb_cByteArray;
309
52
  break;
310
- }
311
- default: {
53
+
54
+ default:
312
55
  type = rb_cString;
313
56
  break;
314
- }
315
57
  }
316
58
  }
317
59
 
318
60
  if (type == rb_cInteger) {
319
61
  return LL2NUM(sqlite3_column_int64(stmt, i));
320
- } else if (type == rb_cString) {
62
+ }
63
+ else if (type == rb_cString) {
321
64
  return DO_STR_NEW((char*)sqlite3_column_text(stmt, i), length, encoding, internal_encoding);
322
- } else if (type == rb_cFloat) {
65
+ }
66
+ else if (type == rb_cFloat) {
323
67
  return rb_float_new(sqlite3_column_double(stmt, i));
324
- } else if (type == rb_cBigDecimal) {
68
+ }
69
+ else if (type == rb_cBigDecimal) {
325
70
  return rb_funcall(rb_cBigDecimal, ID_NEW, 1, rb_str_new((char*)sqlite3_column_text(stmt, i), length));
326
- } else if (type == rb_cDate) {
71
+ }
72
+ else if (type == rb_cDate) {
327
73
  return parse_date((char*)sqlite3_column_text(stmt, i));
328
- } else if (type == rb_cDateTime) {
74
+ }
75
+ else if (type == rb_cDateTime) {
329
76
  return parse_date_time((char*)sqlite3_column_text(stmt, i));
330
- } else if (type == rb_cTime) {
77
+ }
78
+ else if (type == rb_cTime) {
331
79
  return parse_time((char*)sqlite3_column_text(stmt, i));
332
- } else if (type == rb_cTrueClass) {
80
+ }
81
+ else if (type == rb_cTrueClass) {
333
82
  return strcmp((char*)sqlite3_column_text(stmt, i), "t") == 0 ? Qtrue : Qfalse;
334
- } else if (type == rb_cByteArray) {
83
+ }
84
+ else if (type == rb_cByteArray) {
335
85
  return rb_funcall(rb_cByteArray, ID_NEW, 1, rb_str_new((char*)sqlite3_column_blob(stmt, i), length));
336
- } else if (type == rb_cClass) {
86
+ }
87
+ else if (type == rb_cClass) {
337
88
  return rb_funcall(mDO, rb_intern("full_const_get"), 1, rb_str_new((char*)sqlite3_column_text(stmt, i), length));
338
- } else if (type == rb_cNilClass) {
89
+ }
90
+ else if (type == rb_cNilClass) {
339
91
  return Qnil;
340
- } else {
92
+ }
93
+ else {
341
94
  return DO_STR_NEW((char*)sqlite3_column_text(stmt, i), length, encoding, internal_encoding);
342
95
  }
343
96
  }
@@ -346,9 +99,8 @@ static VALUE typecast(sqlite3_stmt *stmt, int i, VALUE type, int encoding) {
346
99
 
347
100
  #define FLAG_PRESENT(query_values, flag) !NIL_P(rb_hash_aref(query_values, flag))
348
101
 
349
- static int flags_from_uri(VALUE uri) {
102
+ int flags_from_uri(VALUE uri) {
350
103
  VALUE query_values = rb_funcall(uri, rb_intern("query"), 0);
351
-
352
104
  int flags = 0;
353
105
 
354
106
  if (!NIL_P(query_values) && TYPE(query_values) == T_HASH) {
@@ -356,22 +108,27 @@ static int flags_from_uri(VALUE uri) {
356
108
  #ifdef SQLITE_OPEN_READONLY
357
109
  if (FLAG_PRESENT(query_values, OPEN_FLAG_READONLY)) {
358
110
  flags |= SQLITE_OPEN_READONLY;
359
- } else {
111
+ }
112
+ else {
360
113
  flags |= SQLITE_OPEN_READWRITE;
361
114
  }
362
115
  #endif
116
+
363
117
  #ifdef SQLITE_OPEN_NOMUTEX
364
118
  if (FLAG_PRESENT(query_values, OPEN_FLAG_NO_MUTEX)) {
365
119
  flags |= SQLITE_OPEN_NOMUTEX;
366
120
  }
367
121
  #endif
122
+
368
123
  #ifdef SQLITE_OPEN_FULLMUTEX
369
124
  if (FLAG_PRESENT(query_values, OPEN_FLAG_FULL_MUTEX)) {
370
125
  flags |= SQLITE_OPEN_FULLMUTEX;
371
126
  }
372
127
  #endif
128
+
373
129
  flags |= SQLITE_OPEN_CREATE;
374
- } else {
130
+ }
131
+ else {
375
132
  flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
376
133
  }
377
134
 
@@ -382,12 +139,10 @@ static int flags_from_uri(VALUE uri) {
382
139
 
383
140
  /****** Public API ******/
384
141
 
385
- static VALUE cConnection_initialize(VALUE self, VALUE uri) {
142
+ VALUE cConnection_initialize(VALUE self, VALUE uri) {
143
+ VALUE path = rb_funcall(uri, rb_intern("path"), 0);
144
+ sqlite3 *db = NULL;
386
145
  int ret;
387
- VALUE path;
388
- sqlite3 *db;
389
-
390
- path = rb_funcall(uri, rb_intern("path"), 0);
391
146
 
392
147
  #ifdef HAVE_SQLITE3_OPEN_V2
393
148
  ret = sqlite3_open_v2(StringValuePtr(path), &db, flags_from_uri(uri), 0);
@@ -395,7 +150,7 @@ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
395
150
  ret = sqlite3_open(StringValuePtr(path), &db);
396
151
  #endif
397
152
 
398
- if ( ret != SQLITE_OK ) {
153
+ if (ret != SQLITE_OK) {
399
154
  raise_error(self, db, Qnil);
400
155
  }
401
156
 
@@ -410,72 +165,40 @@ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
410
165
  return Qtrue;
411
166
  }
412
167
 
413
- static VALUE cConnection_dispose(VALUE self) {
168
+ VALUE cConnection_dispose(VALUE self) {
414
169
  VALUE connection_container = rb_iv_get(self, "@connection");
415
170
 
416
- sqlite3 *db;
417
-
418
- if (Qnil == connection_container)
171
+ if (connection_container == Qnil) {
419
172
  return Qfalse;
173
+ }
420
174
 
421
- db = DATA_PTR(connection_container);
175
+ sqlite3 *db = DATA_PTR(connection_container);
422
176
 
423
- if (NULL == db)
177
+ if (!db) {
424
178
  return Qfalse;
179
+ }
425
180
 
426
181
  sqlite3_close(db);
427
182
  rb_iv_set(self, "@connection", Qnil);
428
-
429
183
  return Qtrue;
430
-
431
- }
432
-
433
- static VALUE cCommand_set_types(int argc, VALUE *argv, VALUE self) {
434
- VALUE type_strings = rb_ary_new();
435
- VALUE array = rb_ary_new();
436
-
437
- int i, j;
438
-
439
- for ( i = 0; i < argc; i++) {
440
- rb_ary_push(array, argv[i]);
441
- }
442
-
443
- for (i = 0; i < RARRAY_LEN(array); i++) {
444
- VALUE entry = rb_ary_entry(array, i);
445
- if(TYPE(entry) == T_CLASS) {
446
- rb_ary_push(type_strings, entry);
447
- } else if (TYPE(entry) == T_ARRAY) {
448
- for (j = 0; j < RARRAY_LEN(entry); j++) {
449
- VALUE sub_entry = rb_ary_entry(entry, j);
450
- if(TYPE(sub_entry) == T_CLASS) {
451
- rb_ary_push(type_strings, sub_entry);
452
- } else {
453
- rb_raise(rb_eArgError, "Invalid type given");
454
- }
455
- }
456
- } else {
457
- rb_raise(rb_eArgError, "Invalid type given");
458
- }
459
- }
460
-
461
- rb_iv_set(self, "@field_types", type_strings);
462
-
463
- return array;
464
184
  }
465
185
 
466
- static VALUE cConnection_quote_boolean(VALUE self, VALUE value) {
186
+ VALUE cConnection_quote_boolean(VALUE self, VALUE value) {
467
187
  return rb_str_new2(value == Qtrue ? "'t'" : "'f'");
468
188
  }
469
189
 
470
- static VALUE cConnection_quote_string(VALUE self, VALUE string) {
190
+ VALUE cConnection_quote_string(VALUE self, VALUE string) {
471
191
  const char *source = rb_str_ptr_readonly(string);
472
- char *escaped_with_quotes;
473
- VALUE result;
474
192
 
475
193
  // Wrap the escaped string in single-quotes, this is DO's convention
476
- escaped_with_quotes = sqlite3_mprintf("%Q", source);
194
+ char *escaped_with_quotes = sqlite3_mprintf("%Q", source);
195
+
196
+ if(!escaped_with_quotes) {
197
+ rb_memerror();
198
+ }
199
+
200
+ VALUE result = rb_str_new2(escaped_with_quotes);
477
201
 
478
- result = rb_str_new2(escaped_with_quotes);
479
202
  #ifdef HAVE_RUBY_ENCODING_H
480
203
  rb_enc_associate_index(result, FIX2INT(rb_iv_get(self, "@encoding_id")));
481
204
  #endif
@@ -483,209 +206,176 @@ static VALUE cConnection_quote_string(VALUE self, VALUE string) {
483
206
  return result;
484
207
  }
485
208
 
486
- static VALUE cConnection_quote_byte_array(VALUE self, VALUE string) {
209
+ VALUE cConnection_quote_byte_array(VALUE self, VALUE string) {
487
210
  VALUE source = StringValue(string);
488
211
  VALUE array = rb_funcall(source, rb_intern("unpack"), 1, rb_str_new2("H*"));
212
+
489
213
  rb_ary_unshift(array, rb_str_new2("X'"));
490
214
  rb_ary_push(array, rb_str_new2("'"));
491
215
  return rb_ary_join(array, Qnil);
492
216
  }
493
217
 
494
- static VALUE cConnection_character_set(VALUE self) {
495
- return rb_iv_get(self, "@encoding");
496
- }
497
-
498
- static VALUE cConnection_enable_load_extension(VALUE self, VALUE value) {
499
- VALUE connection;
500
- int status;
501
- sqlite3 *db;
218
+ VALUE cConnection_enable_load_extension(VALUE self, VALUE value) {
219
+ VALUE connection = rb_iv_get(self, "@connection");
502
220
 
503
- connection = rb_iv_get(self, "@connection");
504
-
505
- if (Qnil == connection)
221
+ if (connection == Qnil) {
506
222
  return Qfalse;
223
+ }
507
224
 
508
- db = DATA_PTR(connection);
225
+ sqlite3 *db = DATA_PTR(connection);
509
226
 
510
- if (NULL == db)
227
+ if (!db) {
511
228
  return Qfalse;
229
+ }
512
230
 
513
- status = sqlite3_enable_load_extension(db, value == Qtrue ? 1 : 0);
514
- if ( status != SQLITE_OK ) {
231
+ int status = sqlite3_enable_load_extension(db, value == Qtrue ? 1 : 0);
232
+
233
+ if (status != SQLITE_OK) {
515
234
  rb_raise(eConnectionError, "Error enabling load extension.");
516
235
  }
517
236
  return Qtrue;
518
237
  }
519
238
 
520
- static VALUE cConnection_load_extension(VALUE self, VALUE string) {
521
- VALUE connection;
522
- sqlite3 *db;
523
- const char *extension_name = rb_str_ptr_readonly(string);
524
- char* errmsg;
525
- int status;
239
+ VALUE cConnection_load_extension(VALUE self, VALUE string) {
240
+ VALUE connection = rb_iv_get(self, "@connection");
526
241
 
527
- connection = rb_iv_get(self, "@connection");
528
-
529
- if (Qnil == connection)
242
+ if (connection == Qnil) {
530
243
  return Qfalse;
244
+ }
531
245
 
532
- db = DATA_PTR(connection);
246
+ sqlite3 *db = DATA_PTR(connection);
533
247
 
534
- if (NULL == db)
248
+ if (!db) {
535
249
  return Qfalse;
250
+ }
251
+
252
+ const char *extension_name = rb_str_ptr_readonly(string);
253
+ char *errmsg = NULL;
254
+ int status = sqlite3_load_extension(db, extension_name, 0, &errmsg);
536
255
 
537
- status = sqlite3_load_extension(db, extension_name, 0, &errmsg);
538
- if ( status != SQLITE_OK ) {
256
+ if (status != SQLITE_OK) {
539
257
  rb_raise(eConnectionError, "%s", errmsg);
540
258
  }
541
259
  return Qtrue;
542
260
  }
543
261
 
544
- static VALUE build_query_from_args(VALUE klass, int count, VALUE *args) {
545
- VALUE query = rb_iv_get(klass, "@text");
546
- int i;
547
- VALUE array = rb_ary_new();
548
- for ( i = 0; i < count; i++) {
549
- rb_ary_push(array, (VALUE)args[i]);
550
- }
551
- query = rb_funcall(klass, ID_ESCAPE, 1, array);
552
- return query;
553
- }
554
-
555
- static VALUE cCommand_execute_non_query(int argc, VALUE *argv, VALUE self) {
556
- sqlite3 *db;
557
- char *error_message;
558
- int status;
559
- int affected_rows;
560
- do_int64 insert_id;
561
- VALUE connection, sqlite3_connection;
562
- VALUE query;
563
- struct timeval start;
564
-
565
- query = build_query_from_args(self, argc, argv);
262
+ VALUE cCommand_execute_non_query(int argc, VALUE *argv, VALUE self) {
263
+ VALUE query = build_query_from_args(self, argc, argv);
264
+ VALUE connection = rb_iv_get(self, "@connection");
265
+ VALUE sqlite3_connection = rb_iv_get(connection, "@connection");
566
266
 
567
- connection = rb_iv_get(self, "@connection");
568
- sqlite3_connection = rb_iv_get(connection, "@connection");
569
- if (Qnil == sqlite3_connection) {
267
+ if (sqlite3_connection == Qnil) {
570
268
  rb_raise(eConnectionError, "This connection has already been closed.");
571
269
  }
572
270
 
271
+ sqlite3 *db = NULL;
272
+
573
273
  Data_Get_Struct(sqlite3_connection, sqlite3, db);
574
274
 
275
+ struct timeval start;
276
+ char *error_message;
277
+ int status;
278
+
575
279
  gettimeofday(&start, NULL);
576
280
  status = sqlite3_exec(db, rb_str_ptr_readonly(query), 0, 0, &error_message);
577
281
 
578
- if ( status != SQLITE_OK ) {
282
+ if (status != SQLITE_OK) {
579
283
  raise_error(self, db, query);
580
284
  }
285
+
581
286
  data_objects_debug(connection, query, &start);
582
287
 
583
- affected_rows = sqlite3_changes(db);
584
- insert_id = sqlite3_last_insert_rowid(db);
288
+ int affected_rows = sqlite3_changes(db);
289
+ do_int64 insert_id = sqlite3_last_insert_rowid(db);
585
290
 
586
291
  return rb_funcall(cResult, ID_NEW, 3, self, INT2NUM(affected_rows), INT2NUM(insert_id));
587
292
  }
588
293
 
589
- static VALUE cCommand_execute_reader(int argc, VALUE *argv, VALUE self) {
590
- sqlite3 *db;
591
- sqlite3_stmt *sqlite3_reader;
592
- int status;
593
- int field_count;
594
- int i;
595
- VALUE reader;
596
- VALUE connection, sqlite3_connection;
597
- VALUE query;
598
- VALUE field_names, field_types;
599
- struct timeval start;
294
+ VALUE cCommand_execute_reader(int argc, VALUE *argv, VALUE self) {
295
+ VALUE query = build_query_from_args(self, argc, argv);
296
+ VALUE connection = rb_iv_get(self, "@connection");
297
+ VALUE sqlite3_connection = rb_iv_get(connection, "@connection");
600
298
 
601
- connection = rb_iv_get(self, "@connection");
602
- sqlite3_connection = rb_iv_get(connection, "@connection");
603
- if (Qnil == sqlite3_connection) {
299
+ if (sqlite3_connection == Qnil) {
604
300
  rb_raise(eConnectionError, "This connection has already been closed.");
605
301
  }
606
302
 
303
+ sqlite3 *db = NULL;
304
+
607
305
  Data_Get_Struct(sqlite3_connection, sqlite3, db);
608
306
 
609
- query = build_query_from_args(self, argc, argv);
307
+ sqlite3_stmt *sqlite3_reader;
308
+ struct timeval start;
309
+ int status;
610
310
 
611
311
  gettimeofday(&start, NULL);
612
312
  status = sqlite3_prepare_v2(db, rb_str_ptr_readonly(query), -1, &sqlite3_reader, 0);
613
313
  data_objects_debug(connection, query, &start);
614
314
 
615
- if ( status != SQLITE_OK ) {
315
+ if (status != SQLITE_OK) {
616
316
  raise_error(self, db, query);
617
317
  }
618
318
 
619
- field_count = sqlite3_column_count(sqlite3_reader);
620
- reader = rb_funcall(cReader, ID_NEW, 0);
319
+ int field_count = sqlite3_column_count(sqlite3_reader);
320
+ VALUE reader = rb_funcall(cReader, ID_NEW, 0);
621
321
 
622
322
  rb_iv_set(reader, "@reader", Data_Wrap_Struct(rb_cObject, 0, 0, sqlite3_reader));
623
323
  rb_iv_set(reader, "@field_count", INT2NUM(field_count));
624
324
  rb_iv_set(reader, "@connection", connection);
625
325
 
626
- field_names = rb_ary_new();
627
- field_types = rb_iv_get(self, "@field_types");
326
+ VALUE field_types = rb_iv_get(self, "@field_types");
628
327
 
629
- if ( field_types == Qnil || 0 == RARRAY_LEN(field_types) ) {
328
+ if (field_types == Qnil || RARRAY_LEN(field_types) == 0) {
630
329
  field_types = rb_ary_new();
631
- } else if (RARRAY_LEN(field_types) != field_count) {
330
+ }
331
+ else if (RARRAY_LEN(field_types) != field_count) {
632
332
  // Whoops... wrong number of types passed to set_types. Close the reader and raise
633
333
  // and error
634
334
  rb_funcall(reader, rb_intern("close"), 0);
635
335
  rb_raise(rb_eArgError, "Field-count mismatch. Expected %ld fields, but the query yielded %d", RARRAY_LEN(field_types), field_count);
636
336
  }
637
337
 
638
- for ( i = 0; i < field_count; i++ ) {
338
+ VALUE field_names = rb_ary_new();
339
+ int i;
340
+
341
+ for (i = 0; i < field_count; i++) {
639
342
  rb_ary_push(field_names, rb_str_new2((char *)sqlite3_column_name(sqlite3_reader, i)));
640
343
  }
641
344
 
642
345
  rb_iv_set(reader, "@fields", field_names);
643
346
  rb_iv_set(reader, "@field_types", field_types);
644
-
645
347
  return reader;
646
348
  }
647
349
 
648
- static VALUE cReader_close(VALUE self) {
350
+ VALUE cReader_close(VALUE self) {
649
351
  VALUE reader_obj = rb_iv_get(self, "@reader");
650
352
 
651
- if ( reader_obj != Qnil ) {
652
- sqlite3_stmt *reader;
353
+ if (reader_obj != Qnil) {
354
+ sqlite3_stmt *reader = NULL;
355
+
653
356
  Data_Get_Struct(reader_obj, sqlite3_stmt, reader);
654
357
  sqlite3_finalize(reader);
655
358
  rb_iv_set(self, "@reader", Qnil);
656
359
  return Qtrue;
657
360
  }
658
- else {
659
- return Qfalse;
660
- }
661
- }
662
361
 
663
- static VALUE cReader_next(VALUE self) {
664
- sqlite3_stmt *reader;
665
- int field_count;
666
- int result;
667
- int i;
668
- size_t ft_length;
669
- VALUE arr = rb_ary_new();
670
- VALUE field_types;
671
- VALUE field_type;
672
- VALUE value;
362
+ return Qfalse;
363
+ }
673
364
 
674
- if(rb_iv_get(self, "@done") == Qtrue) {
365
+ VALUE cReader_next(VALUE self) {
366
+ if (rb_iv_get(self, "@done") == Qtrue) {
675
367
  return Qfalse;
676
368
  }
677
369
 
678
- Data_Get_Struct(rb_iv_get(self, "@reader"), sqlite3_stmt, reader);
679
- field_count = NUM2INT(rb_iv_get(self, "@field_count"));
370
+ sqlite3_stmt *reader = NULL;
371
+ int result;
680
372
 
681
- field_types = rb_iv_get(self, "@field_types");
682
- ft_length = RARRAY_LEN(field_types);
373
+ Data_Get_Struct(rb_iv_get(self, "@reader"), sqlite3_stmt, reader);
683
374
 
684
375
  result = sqlite3_step(reader);
685
-
686
376
  rb_iv_set(self, "@state", INT2NUM(result));
687
377
 
688
- if ( result != SQLITE_ROW ) {
378
+ if (result != SQLITE_ROW) {
689
379
  rb_iv_set(self, "@values", Qnil);
690
380
  rb_iv_set(self, "@done", Qtrue);
691
381
  return Qfalse;
@@ -694,86 +384,46 @@ static VALUE cReader_next(VALUE self) {
694
384
  int enc = -1;
695
385
  #ifdef HAVE_RUBY_ENCODING_H
696
386
  VALUE encoding_id = rb_iv_get(rb_iv_get(self, "@connection"), "@encoding_id");
387
+
697
388
  if (encoding_id != Qnil) {
698
389
  enc = FIX2INT(encoding_id);
699
390
  }
700
391
  #endif
701
392
 
393
+ VALUE field_types = rb_iv_get(self, "@field_types");
394
+ int field_count = NUM2INT(rb_iv_get(self, "@field_count"));
395
+ VALUE arr = rb_ary_new();
396
+ VALUE field_type;
397
+ VALUE value;
398
+ int i;
702
399
 
703
- for ( i = 0; i < field_count; i++ ) {
400
+ for (i = 0; i < field_count; i++) {
704
401
  field_type = rb_ary_entry(field_types, i);
705
402
  value = typecast(reader, i, field_type, enc);
706
403
  rb_ary_push(arr, value);
707
404
  }
708
405
 
709
406
  rb_iv_set(self, "@values", arr);
710
-
711
407
  return Qtrue;
712
408
  }
713
409
 
714
- static VALUE cReader_values(VALUE self) {
410
+ VALUE cReader_values_sqlite(VALUE self) {
715
411
  VALUE state = rb_iv_get(self, "@state");
716
- if ( state == Qnil || NUM2INT(state) != SQLITE_ROW ) {
412
+
413
+ if (state == Qnil || NUM2INT(state) != SQLITE_ROW) {
717
414
  rb_raise(eDataError, "Reader is not initialized");
718
415
  return Qnil;
719
416
  }
720
- else {
721
- return rb_iv_get(self, "@values");
722
- }
723
- }
724
-
725
- static VALUE cReader_fields(VALUE self) {
726
- return rb_iv_get(self, "@fields");
727
- }
728
417
 
729
- static VALUE cReader_field_count(VALUE self) {
730
- return rb_iv_get(self, "@field_count");
418
+ return rb_iv_get(self, "@values");
731
419
  }
732
420
 
733
421
  void Init_do_sqlite3() {
734
- rb_require("bigdecimal");
735
- rb_require("date");
736
- rb_require("rational");
737
- rb_require("data_objects");
738
-
739
- ID_CONST_GET = rb_intern("const_get");
740
-
741
- // Get references classes needed for Date/Time parsing
742
- rb_cDate = CONST_GET(rb_mKernel, "Date");
743
- rb_cDateTime = CONST_GET(rb_mKernel, "DateTime");
744
- rb_cBigDecimal = CONST_GET(rb_mKernel, "BigDecimal");
422
+ common_init();
745
423
 
746
- #ifdef RUBY_LESS_THAN_186
747
- ID_NEW_DATE = rb_intern("new0");
748
- #else
749
- ID_NEW_DATE = rb_intern("new!");
750
- #endif
751
- ID_RATIONAL = rb_intern("Rational");
752
- ID_NEW = rb_intern("new");
753
- ID_ESCAPE = rb_intern("escape_sql");
754
- ID_LOG = rb_intern("log");
755
-
756
- // Get references to the Extlib module
757
- mExtlib = CONST_GET(rb_mKernel, "Extlib");
758
- rb_cByteArray = CONST_GET(mExtlib, "ByteArray");
759
-
760
- // Get references to the DataObjects module and its classes
761
- mDO = CONST_GET(rb_mKernel, "DataObjects");
762
- cDO_Quoting = CONST_GET(mDO, "Quoting");
763
- cDO_Connection = CONST_GET(mDO, "Connection");
764
- cDO_Command = CONST_GET(mDO, "Command");
765
- cDO_Result = CONST_GET(mDO, "Result");
766
- cDO_Reader = CONST_GET(mDO, "Reader");
767
- cDO_Logger = CONST_GET(mDO, "Logger");
768
- cDO_Logger_Message = CONST_GET(cDO_Logger, "Message");
769
-
770
- // Initialize the DataObjects::Sqlite3 module, and define its classes
771
424
  mSqlite3 = rb_define_module_under(mDO, "Sqlite3");
772
425
 
773
- eConnectionError = CONST_GET(mDO, "ConnectionError");
774
- eDataError = CONST_GET(mDO, "DataError");
775
-
776
- cConnection = DRIVER_CLASS("Connection", cDO_Connection);
426
+ cConnection = rb_define_class_under(mSqlite3, "Connection", cDO_Connection);
777
427
  rb_define_method(cConnection, "initialize", cConnection_initialize, 1);
778
428
  rb_define_method(cConnection, "dispose", cConnection_dispose, 0);
779
429
  rb_define_method(cConnection, "quote_boolean", cConnection_quote_boolean, 1);
@@ -783,41 +433,23 @@ void Init_do_sqlite3() {
783
433
  rb_define_method(cConnection, "enable_load_extension", cConnection_enable_load_extension, 1);
784
434
  rb_define_method(cConnection, "load_extension", cConnection_load_extension, 1);
785
435
 
786
- cCommand = DRIVER_CLASS("Command", cDO_Command);
436
+ cCommand = rb_define_class_under(mSqlite3, "Command", cDO_Command);
787
437
  rb_define_method(cCommand, "set_types", cCommand_set_types, -1);
788
438
  rb_define_method(cCommand, "execute_non_query", cCommand_execute_non_query, -1);
789
439
  rb_define_method(cCommand, "execute_reader", cCommand_execute_reader, -1);
790
440
 
791
- cResult = DRIVER_CLASS("Result", cDO_Result);
441
+ cResult = rb_define_class_under(mSqlite3, "Result", cDO_Result);
792
442
 
793
- cReader = DRIVER_CLASS("Reader", cDO_Reader);
443
+ cReader = rb_define_class_under(mSqlite3, "Reader", cDO_Reader);
794
444
  rb_define_method(cReader, "close", cReader_close, 0);
795
445
  rb_define_method(cReader, "next!", cReader_next, 0);
796
- rb_define_method(cReader, "values", cReader_values, 0);
446
+ rb_define_method(cReader, "values", cReader_values_sqlite, 0); // TODO: DRY?
797
447
  rb_define_method(cReader, "fields", cReader_fields, 0);
798
448
  rb_define_method(cReader, "field_count", cReader_field_count, 0);
799
449
 
800
- rb_global_variable(&ID_NEW_DATE);
801
- rb_global_variable(&ID_RATIONAL);
802
- rb_global_variable(&ID_CONST_GET);
803
- rb_global_variable(&ID_ESCAPE);
804
- rb_global_variable(&ID_LOG);
805
- rb_global_variable(&ID_NEW);
806
-
807
- rb_global_variable(&rb_cDate);
808
- rb_global_variable(&rb_cDateTime);
809
- rb_global_variable(&rb_cBigDecimal);
810
- rb_global_variable(&rb_cByteArray);
811
-
812
- rb_global_variable(&mDO);
813
- rb_global_variable(&cDO_Logger_Message);
814
-
815
450
  rb_global_variable(&cResult);
816
451
  rb_global_variable(&cReader);
817
452
 
818
- rb_global_variable(&eConnectionError);
819
- rb_global_variable(&eDataError);
820
-
821
453
  OPEN_FLAG_READONLY = rb_str_new2("read_only");
822
454
  rb_global_variable(&OPEN_FLAG_READONLY);
823
455
  OPEN_FLAG_READWRITE = rb_str_new2("read_write");
@@ -830,4 +462,6 @@ void Init_do_sqlite3() {
830
462
  rb_global_variable(&OPEN_FLAG_FULL_MUTEX);
831
463
 
832
464
  Init_do_sqlite3_extension();
465
+
466
+ do_define_errors(mSqlite3, errors);
833
467
  }