swift-db-postgres 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ // vim:ts=4:sts=4:sw=4:expandtab
2
+
3
+ // (c) Bharanee Rathna 2012
4
+
5
+ #pragma once
6
+
7
+ #include "common.h"
8
+
9
+ typedef struct Adapter {
10
+ PGconn *connection;
11
+ int t_nesting;
12
+ } Adapter;
13
+
14
+ void init_swift_db_postgres_adapter();
@@ -0,0 +1,59 @@
1
+ // vim:ts=4:sts=4:sw=4:expandtab
2
+
3
+ // (c) Bharanee Rathna 2012
4
+
5
+ #include "common.h"
6
+ #include <uuid/uuid.h>
7
+
8
+ VALUE rb_uuid_string() {
9
+ size_t n;
10
+ uuid_t uuid;
11
+ char uuid_hex[sizeof(uuid_t) * 2 + 1];
12
+
13
+ uuid_generate(uuid);
14
+ for (n = 0; n < sizeof(uuid_t); n++)
15
+ sprintf(uuid_hex + n * 2 + 1, "%02x", uuid[n]);
16
+
17
+ uuid_hex[0] = 'u';
18
+ return rb_str_new(uuid_hex, sizeof(uuid_t) * 2 + 1);
19
+ }
20
+
21
+ /* NOTE: very naive, no regex etc. */
22
+ /* TODO: a better ragel based replace thingamajigy */
23
+ VALUE db_postgres_normalized_sql(VALUE sql) {
24
+ int i = 0, j = 0, n = 1;
25
+ char normalized[RSTRING_LEN(sql) * 2], *ptr = RSTRING_PTR(sql);
26
+
27
+ while (i < RSTRING_LEN(sql)) {
28
+ if (*ptr == '?')
29
+ j += snprintf(normalized + j, 4, "$%d", n++);
30
+ else
31
+ normalized[j++] = *ptr;
32
+ ptr++;
33
+ i++;
34
+ }
35
+
36
+ return rb_str_new(normalized, j);
37
+ }
38
+
39
+ void db_postgres_check_result(PGresult *result) {
40
+ VALUE error;
41
+ switch (PQresultStatus(result)) {
42
+ case PGRES_TUPLES_OK:
43
+ case PGRES_COPY_OUT:
44
+ case PGRES_COPY_IN:
45
+ case PGRES_EMPTY_QUERY:
46
+ case PGRES_COMMAND_OK:
47
+ break;
48
+ case PGRES_BAD_RESPONSE:
49
+ case PGRES_FATAL_ERROR:
50
+ case PGRES_NONFATAL_ERROR:
51
+ error = rb_str_new2(PQresultErrorMessage(result));
52
+ PQclear(result);
53
+ rb_raise(strstr(CSTRING(error), "bind message") ? eSwiftArgumentError : eSwiftRuntimeError, "%s", CSTRING(error));
54
+ break;
55
+ default:
56
+ PQclear(result);
57
+ rb_raise(eSwiftRuntimeError, "unknown error, check logs");
58
+ }
59
+ }
@@ -0,0 +1,35 @@
1
+ #pragma once
2
+
3
+ #define DLL_PRIVATE __attribute__ ((visibility ("hidden")))
4
+ #define CONST_GET(scope, constant) rb_funcall(scope, rb_intern("const_get"), 1, rb_str_new2(constant))
5
+ #define TO_S(v) rb_funcall(v, rb_intern("to_s"), 0)
6
+ #define CSTRING(v) RSTRING_PTR(TO_S(v))
7
+
8
+ #include <ruby/ruby.h>
9
+ #include <ruby/encoding.h>
10
+
11
+ #include <libpq-fe.h>
12
+ #include <libpq/libpq-fs.h>
13
+
14
+ #include <fcntl.h>
15
+ #include <sys/stat.h>
16
+ #include <sys/types.h>
17
+ #include <time.h>
18
+ #include <unistd.h>
19
+
20
+ extern VALUE mSwift, mDB;
21
+ extern VALUE cDPA, cDPS, cDPR;
22
+ extern VALUE eSwiftError, eSwiftArgumentError, eSwiftRuntimeError, eSwiftConnectionError;
23
+ extern VALUE cStringIO;
24
+
25
+ DLL_PRIVATE VALUE rb_uuid_string();
26
+ DLL_PRIVATE VALUE db_postgres_normalized_sql(VALUE);
27
+ DLL_PRIVATE void db_postgres_check_result(PGresult *);
28
+
29
+ typedef struct Query {
30
+ PGconn *connection;
31
+ char *command;
32
+ int n_args;
33
+ char **data;
34
+ int *size, *format;
35
+ } Query;
@@ -0,0 +1,99 @@
1
+ #include "datetime.h"
2
+ #include <ctype.h>
3
+
4
+ extern VALUE dtformat;
5
+
6
+ VALUE cSwiftDateTime, day_seconds;
7
+ ID fcivil, fparse, fstrptime;
8
+
9
+ // NOTE: only parses '%F %T.%N %z' format and falls back to the built-in DateTime#parse
10
+ // and is almost 2x faster than doing:
11
+ //
12
+ // rb_funcall(klass, fstrptime, 2, rb_str_new(data, size), dtformat);
13
+ //
14
+ VALUE datetime_parse(VALUE klass, const char *data, size_t size) {
15
+ struct tm tm;
16
+ double seconds;
17
+ const char *ptr;
18
+ char tzsign = 0, fraction[32];
19
+ int tzhour = 0, tzmin = 0, lastmatch = -1, offset = 0, idx;
20
+
21
+ memset(&tm, 0, sizeof(struct tm));
22
+ sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d%n",
23
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &lastmatch);
24
+
25
+ // fallback to default datetime parser, this is more expensive.
26
+ if (tm.tm_mday == 0)
27
+ return Qnil;
28
+
29
+ seconds = tm.tm_sec;
30
+
31
+ // parse millisecs if any -- tad faster than using %lf in sscanf above.
32
+ if (lastmatch > 0 && lastmatch < (int)size && *(data + lastmatch) == '.') {
33
+ idx = 0;
34
+ ptr = data + ++lastmatch;
35
+ while (*ptr && isdigit(*ptr) && idx < 31) {
36
+ lastmatch++;
37
+ fraction[idx++] = *ptr++;
38
+ }
39
+
40
+ fraction[idx] = 0;
41
+ seconds += (double)atoll(fraction) / pow(10, idx);
42
+ }
43
+
44
+ // parse timezone offsets if any - matches +HH:MM +HH MM +HHMM
45
+ if (lastmatch > 0 && lastmatch < (int)size) {
46
+ const char *ptr = data + lastmatch;
47
+ while(*ptr && *ptr != '+' && *ptr != '-') ptr++;
48
+ tzsign = *ptr++;
49
+ if (*ptr && isdigit(*ptr)) {
50
+ tzhour = *ptr++ - '0';
51
+ if (*ptr && isdigit(*ptr)) tzhour = tzhour * 10 + *ptr++ - '0';
52
+ while(*ptr && !isdigit(*ptr)) ptr++;
53
+ if (*ptr) {
54
+ tzmin = *ptr++ - '0';
55
+ if (*ptr && isdigit(*ptr)) tzmin = tzmin * 10 + *ptr++ - '0';
56
+ }
57
+ }
58
+ }
59
+
60
+ if (tzsign) {
61
+ offset = tzsign == '+'
62
+ ? (time_t)tzhour * 3600 + (time_t)tzmin * 60
63
+ : (time_t)tzhour * -3600 + (time_t)tzmin * -60;
64
+ }
65
+
66
+ return rb_funcall(klass, fcivil, 7,
67
+ INT2FIX(tm.tm_year), INT2FIX(tm.tm_mon), INT2FIX(tm.tm_mday),
68
+ INT2FIX(tm.tm_hour), INT2FIX(tm.tm_min), DBL2NUM(seconds),
69
+ offset == 0 ? INT2FIX(0) : rb_Rational(INT2FIX(offset), day_seconds)
70
+ );
71
+ }
72
+
73
+ VALUE rb_datetime_parse(VALUE self, VALUE string) {
74
+ VALUE datetime;
75
+ const char *data = CSTRING(string);
76
+ size_t size = TYPE(string) == T_STRING ? (size_t)RSTRING_LEN(string) : strlen(data);
77
+
78
+ if (NIL_P(string))
79
+ return Qnil;
80
+
81
+ datetime = datetime_parse(self, data, size);
82
+ return NIL_P(datetime) ? rb_call_super(1, &string) : datetime;
83
+ }
84
+
85
+ void init_swift_datetime() {
86
+ VALUE mSwift, cDateTime;
87
+
88
+ rb_require("date");
89
+ mSwift = rb_define_module("Swift");
90
+ cDateTime = CONST_GET(rb_mKernel, "DateTime");
91
+ cSwiftDateTime = rb_define_class_under(mSwift, "DateTime", cDateTime);
92
+ fcivil = rb_intern("civil");
93
+ fparse = rb_intern("parse");
94
+ fstrptime = rb_intern("strptime");
95
+ day_seconds = INT2FIX(86400);
96
+
97
+ rb_global_variable(&day_seconds);
98
+ rb_define_singleton_method(cSwiftDateTime, "parse", RUBY_METHOD_FUNC(rb_datetime_parse), 1);
99
+ }
@@ -0,0 +1,8 @@
1
+ #pragma once
2
+
3
+ #include "common.h"
4
+ #include <math.h>
5
+
6
+ DLL_PRIVATE extern VALUE cSwiftDateTime;
7
+ DLL_PRIVATE void init_swift_datetime();
8
+ DLL_PRIVATE VALUE datetime_parse(VALUE klass, const char *data, size_t size);
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'mkmf'
4
+
5
+ $CFLAGS = '-std=c99'
6
+
7
+ inc_paths = %w(
8
+ /usr/include/postgresql
9
+ /usr/local/include/postgresql
10
+ /opt/local/include
11
+ /opt/local/include/postgresql90
12
+ /opt/local/include/postgresql85
13
+ /opt/local/include/postgresql84
14
+ /sw/include
15
+ )
16
+
17
+ lib_paths = %w(
18
+ /usr/lib
19
+ /usr/local/lib
20
+ /opt/lib
21
+ /opt/local/lib
22
+ /sw/lib
23
+ )
24
+
25
+ (inc_paths << ENV['SPG_INCLUDE_DIRS']).compact!
26
+ (lib_paths << ENV['SPG_LIBRARY_DIRS']).compact!
27
+
28
+ find_header('libpq-fe.h', *inc_paths) or raise 'unable to locate postgresql headers set SPG_INCLUDE_DIRS'
29
+ find_header('uuid/uuid.h', *inc_paths) or raise 'unable to locate uuid headers set SPG_INCLUDE_DIRS'
30
+
31
+ find_library('pq', 'main', *lib_paths) or raise 'unable to locate postgresql lib set SPG_LIBRARY_DIRS'
32
+ find_library('uuid','main', *lib_paths) or raise 'unable to locate uuid lib set SPG_LIBRARY_DIRS'
33
+ create_makefile('swift_db_postgres_ext')
@@ -0,0 +1,28 @@
1
+ // vim:ts=4:sts=4:sw=4:expandtab
2
+
3
+ // (c) Bharanee Rathna 2012
4
+
5
+ #include "common.h"
6
+ #include "adapter.h"
7
+ #include "statement.h"
8
+ #include "result.h"
9
+ #include "datetime.h"
10
+
11
+ VALUE mSwift, mDB;
12
+ VALUE eSwiftError, eSwiftArgumentError, eSwiftRuntimeError, eSwiftConnectionError;
13
+
14
+ void Init_swift_db_postgres_ext() {
15
+ mSwift = rb_define_module("Swift");
16
+ mDB = rb_define_module_under(mSwift, "DB");
17
+
18
+ eSwiftError = rb_define_class_under(mSwift, "Error", rb_eStandardError);
19
+ eSwiftArgumentError = rb_define_class_under(mSwift, "ArgumentError", eSwiftError);
20
+ eSwiftRuntimeError = rb_define_class_under(mSwift, "RuntimeError", eSwiftError);
21
+ eSwiftConnectionError = rb_define_class_under(mSwift, "ConnectionError", eSwiftError);
22
+
23
+ init_swift_db_postgres_adapter();
24
+ init_swift_db_postgres_statement();
25
+ init_swift_db_postgres_result();
26
+ init_swift_datetime();
27
+ init_swift_db_postgres_typecast();
28
+ }
@@ -0,0 +1,172 @@
1
+ // vim:ts=4:sts=4:sw=4:expandtab
2
+
3
+ // (c) Bharanee Rathna 2012
4
+
5
+ #include "result.h"
6
+ #include <stdlib.h>
7
+
8
+ /* declaration */
9
+
10
+ typedef struct Result {
11
+ PGresult *result;
12
+ VALUE fields;
13
+ VALUE types;
14
+ VALUE rows;
15
+ size_t selected;
16
+ size_t affected;
17
+ size_t insert_id;
18
+ } Result;
19
+
20
+ VALUE cDPR;
21
+
22
+ /* definition */
23
+
24
+ Result* db_postgres_result_handle(VALUE self) {
25
+ Result *r;
26
+ Data_Get_Struct(self, Result, r);
27
+ if (!r)
28
+ rb_raise(eSwiftRuntimeError, "Invalid postgres result");
29
+ return r;
30
+ }
31
+
32
+ void db_postgres_result_mark(Result *r) {
33
+ if (r) {
34
+ if (r->fields)
35
+ rb_gc_mark_maybe(r->fields);
36
+ if (r->types)
37
+ rb_gc_mark_maybe(r->types);
38
+ if (r->rows)
39
+ rb_gc_mark_maybe(r->rows);
40
+ }
41
+ }
42
+
43
+ VALUE db_postgres_result_deallocate(Result *r) {
44
+ if (r) {
45
+ if (r->result)
46
+ PQclear(r->result);
47
+ free(r);
48
+ }
49
+ }
50
+
51
+ VALUE db_postgres_result_allocate(VALUE klass) {
52
+ Result *r = (Result*)malloc(sizeof(Result));
53
+ memset(r, 0, sizeof(Result));
54
+ return Data_Wrap_Struct(klass, db_postgres_result_mark, db_postgres_result_deallocate, r);
55
+ }
56
+
57
+ VALUE db_postgres_result_load(VALUE self, PGresult *result) {
58
+ size_t n, rows, cols;
59
+ const char *type, *data;
60
+
61
+ Result *r = db_postgres_result_handle(self);
62
+ r->fields = rb_ary_new();
63
+ r->types = rb_ary_new();
64
+ r->rows = rb_ary_new();
65
+ r->result = result;
66
+ r->affected = atol(PQcmdTuples(result));
67
+ r->selected = PQntuples(result);
68
+ r->insert_id = 0;
69
+
70
+ rows = PQntuples(result);
71
+ cols = PQnfields(result);
72
+ if (rows > 0)
73
+ r->insert_id = PQgetisnull(result, 0, 0) ? 0 : atol(PQgetvalue(result, 0, 0));
74
+
75
+ for (n = 0; n < cols; n++) {
76
+ /* this must be a command execution result without field information */
77
+ if (!(data = PQfname(result, n)))
78
+ break;
79
+ rb_ary_push(r->fields, ID2SYM(rb_intern(data)));
80
+
81
+ switch (PQftype(result, n)) {
82
+ case 16: rb_ary_push(r->types, INT2NUM(SWIFT_TYPE_BOOLEAN)); break;
83
+ case 17: rb_ary_push(r->types, INT2NUM(SWIFT_TYPE_BLOB)); break;
84
+ case 20:
85
+ case 21:
86
+ case 23: rb_ary_push(r->types, INT2NUM(SWIFT_TYPE_INT)); break;
87
+ case 25: rb_ary_push(r->types, INT2NUM(SWIFT_TYPE_TEXT)); break;
88
+ case 700:
89
+ case 701: rb_ary_push(r->types, INT2NUM(SWIFT_TYPE_FLOAT)); break;
90
+ case 1082: rb_ary_push(r->types, INT2NUM(SWIFT_TYPE_DATE)); break;
91
+ case 1114:
92
+ case 1184: rb_ary_push(r->types, INT2NUM(SWIFT_TYPE_TIMESTAMP)); break;
93
+ case 1700: rb_ary_push(r->types, INT2NUM(SWIFT_TYPE_NUMERIC)); break;
94
+ case 1083:
95
+ case 1266: rb_ary_push(r->types, INT2NUM(SWIFT_TYPE_TIME)); break;
96
+ default: rb_ary_push(r->types, INT2NUM(SWIFT_TYPE_TEXT));
97
+ }
98
+ }
99
+
100
+ return self;
101
+ }
102
+
103
+ VALUE db_postgres_result_each(VALUE self) {
104
+ int row, col;
105
+ Result *r = db_postgres_result_handle(self);
106
+
107
+ if (!r->result)
108
+ return Qnil;
109
+
110
+ for (row = 0; row < PQntuples(r->result); row++) {
111
+ VALUE tuple = rb_hash_new();
112
+ for (col = 0; col < PQnfields(r->result); col++) {
113
+ if (PQgetisnull(r->result, row, col))
114
+ rb_hash_aset(tuple, rb_ary_entry(r->fields, col), Qnil);
115
+ else {
116
+ rb_hash_aset(
117
+ tuple,
118
+ rb_ary_entry(r->fields, col),
119
+ typecast_detect(
120
+ PQgetvalue(r->result, row, col),
121
+ PQgetlength(r->result, row, col),
122
+ NUM2INT(rb_ary_entry(r->types, col))
123
+ )
124
+ );
125
+ }
126
+ }
127
+ rb_yield(tuple);
128
+ }
129
+ return Qtrue;
130
+ }
131
+
132
+ VALUE db_postgres_result_selected_rows(VALUE self) {
133
+ Result *r = db_postgres_result_handle(self);
134
+ return SIZET2NUM(r->selected);
135
+ }
136
+
137
+ VALUE db_postgres_result_affected_rows(VALUE self) {
138
+ Result *r = db_postgres_result_handle(self);
139
+ return SIZET2NUM(r->selected > 0 ? 0 : r->affected);
140
+ }
141
+
142
+ VALUE db_postgres_result_fields(VALUE self) {
143
+ Result *r = db_postgres_result_handle(self);
144
+ return r->fields ? r->fields : rb_ary_new();
145
+ }
146
+
147
+ VALUE db_postgres_result_types(VALUE self) {
148
+ Result *r = db_postgres_result_handle(self);
149
+ return r->types ? typecast_description(r->types) : rb_ary_new();
150
+ }
151
+
152
+ VALUE db_postgres_result_insert_id(VALUE self) {
153
+ Result *r = db_postgres_result_handle(self);
154
+ return SIZET2NUM(r->insert_id);
155
+ }
156
+
157
+ void init_swift_db_postgres_result() {
158
+ rb_require("bigdecimal");
159
+ rb_require("stringio");
160
+ rb_require("date");
161
+
162
+ cDPR = rb_define_class_under(cDPA, "Result", rb_cObject);
163
+
164
+ rb_include_module(cDPR, rb_mEnumerable);
165
+ rb_define_alloc_func(cDPR, db_postgres_result_allocate);
166
+ rb_define_method(cDPR, "each", db_postgres_result_each, 0);
167
+ rb_define_method(cDPR, "selected_rows", db_postgres_result_selected_rows, 0);
168
+ rb_define_method(cDPR, "affected_rows", db_postgres_result_affected_rows, 0);
169
+ rb_define_method(cDPR, "fields", db_postgres_result_fields, 0);
170
+ rb_define_method(cDPR, "types", db_postgres_result_types, 0);
171
+ rb_define_method(cDPR, "insert_id", db_postgres_result_insert_id, 0);
172
+ }