mssqlclient 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,38 @@
1
+ using namespace System;
2
+ using namespace System::Reflection;
3
+ using namespace System::Runtime::CompilerServices;
4
+ using namespace System::Runtime::InteropServices;
5
+ using namespace System::Security::Permissions;
6
+
7
+ //
8
+ // General Information about an assembly is controlled through the following
9
+ // set of attributes. Change these attribute values to modify the information
10
+ // associated with an assembly.
11
+ //
12
+ [assembly:AssemblyTitleAttribute("MsSqlAdapter")];
13
+ [assembly:AssemblyDescriptionAttribute("")];
14
+ [assembly:AssemblyConfigurationAttribute("")];
15
+ [assembly:AssemblyCompanyAttribute("")];
16
+ [assembly:AssemblyProductAttribute("MsSqlAdapter")];
17
+ [assembly:AssemblyCopyrightAttribute("Copyright (c) 2006")];
18
+ [assembly:AssemblyTrademarkAttribute("")];
19
+ [assembly:AssemblyCultureAttribute("")];
20
+
21
+ //
22
+ // Version information for an assembly consists of the following four values:
23
+ //
24
+ // Major Version
25
+ // Minor Version
26
+ // Build Number
27
+ // Revision
28
+ //
29
+ // You can specify all the value or you can default the Revision and Build Numbers
30
+ // by using the '*' as shown below:
31
+
32
+ [assembly:AssemblyVersionAttribute("1.0.*")];
33
+
34
+ [assembly:ComVisible(false)];
35
+
36
+ [assembly:CLSCompliantAttribute(true)];
37
+
38
+ [assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)];
@@ -0,0 +1,177 @@
1
+ #include "helpers.h"
2
+
3
+ VALUE g_CBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
4
+ VALUE g_CDate = rb_const_get(rb_cObject, rb_intern("Date"));
5
+ VALUE g_CTime = rb_const_get(rb_cObject, rb_intern("Time"));
6
+ VALUE g_CDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
7
+
8
+ namespace MsSqlClient {
9
+ namespace Helpers {
10
+ VALUE CastToRubyObject(Type^ clr_type, Object^ object) {
11
+ VALUE ruby_value = Qnil;
12
+
13
+ if (object == System::DBNull::Value) {
14
+ ruby_value = Qnil;
15
+ } else if (clr_type == System::DateTime::typeid) {
16
+ return rb_funcall(g_CDate, rb_intern("new"), 3, ToRubyNumber(2006), ToRubyNumber(9), ToRubyNumber(7));
17
+
18
+ gcroot<DateTime^> clr_date_time = (DateTime^)object;
19
+ if (clr_date_time->Hour == 0 && clr_date_time->Minute == 0 && clr_date_time->Second == 0) {
20
+ // Create a Ruby Date Object
21
+ ruby_value = rb_funcall(g_CDate, rb_intern("new"), 3,
22
+ ToRubyNumber(clr_date_time->Year),
23
+ ToRubyNumber(clr_date_time->Month),
24
+ ToRubyNumber(clr_date_time->Day)
25
+ );
26
+ } else {
27
+ // Create a Ruby DateTime Object
28
+ ruby_value = rb_funcall(g_CTime, rb_intern("local"), 3,
29
+ ToRubyNumber(clr_date_time->Year),
30
+ ToRubyNumber(clr_date_time->Month),
31
+ ToRubyNumber(clr_date_time->Day),
32
+ ToRubyNumber(clr_date_time->Hour),
33
+ ToRubyNumber(clr_date_time->Minute),
34
+ ToRubyNumber(clr_date_time->Second)
35
+ );
36
+ }
37
+ } else {
38
+ if (clr_type == Boolean::typeid) ruby_value = Helpers::ToRubyBoolean((Boolean)object);
39
+ if (clr_type == SByte::typeid) ruby_value = Helpers::ToRubyNumber((SByte)object);
40
+ if (clr_type == Int16::typeid) ruby_value = Helpers::ToRubyNumber((Int16)object);
41
+ if (clr_type == Int32::typeid) ruby_value = Helpers::ToRubyNumber((Int32)object);
42
+ if (clr_type == Int64::typeid) ruby_value = Helpers::ToRubyNumber((Int64)object);
43
+ if (clr_type == Byte::typeid) ruby_value = Helpers::ToRubyNumber((Byte)object);
44
+ if (clr_type == UInt16::typeid) ruby_value = Helpers::ToRubyNumber((UInt16)object);
45
+ if (clr_type == UInt32::typeid) ruby_value = Helpers::ToRubyNumber((UInt32)object);
46
+ if (clr_type == UInt64::typeid) ruby_value = Helpers::ToRubyNumber((UInt64)object);
47
+ if (clr_type == String::typeid) ruby_value = Helpers::ToRubyString((String^)object);
48
+ if (clr_type == Single::typeid) ruby_value = Helpers::ToRubyNumber((Single)object);
49
+ if (clr_type == Double::typeid) ruby_value = Helpers::ToRubyNumber((Double)object);
50
+ }
51
+
52
+ return ruby_value;
53
+ }
54
+
55
+ VALUE convert_reader_to_rowset(SqlDataReader^ reader) {
56
+ VALUE rows = rb_ary_new();
57
+
58
+ array<Type^> ^field_types = gcnew array<Type^>(reader->FieldCount);
59
+ array<String^> ^field_names = gcnew array<String^>(reader->FieldCount);
60
+
61
+ for (int ordinal = 0; ordinal < field_types->Length; ordinal++) {
62
+ field_names[ordinal] = reader->GetName(ordinal);
63
+ field_types[ordinal] = reader->GetFieldType(ordinal);
64
+ }
65
+
66
+ while (reader->Read()) {
67
+ VALUE record = rb_hash_new();
68
+ for (int ordinal = 0; ordinal < field_types->Length; ordinal++) {
69
+ gcroot<Object^> object = reader->GetValue(ordinal);
70
+ rb_hash_aset(record, ToRubyString(field_names[ordinal]), CastToRubyObject((Type^)field_types[ordinal], object));
71
+ delete object;
72
+ }
73
+
74
+ rb_ary_push(rows, record);
75
+ }
76
+
77
+ delete field_names;
78
+ delete field_types;
79
+
80
+ return rows;
81
+ }
82
+
83
+ VALUE ToRubyObject(Object^ object) {
84
+ Type^ objectType = object->GetType();
85
+ VALUE ruby_value = Qnil;
86
+
87
+ if (System::DBNull::Value == object) {
88
+ ruby_value = Qnil;
89
+ } else if (object->GetType() == System::Decimal::typeid) {
90
+ ruby_value = rb_float_new(Convert::ToDouble(object));
91
+ } else if (object->GetType() == System::DateTime::typeid) {
92
+ char* native_string;
93
+ ConvertToCharStar("Time.parse(\"" + object->ToString() + "\")", native_string);
94
+ return rb_eval_string(native_string);
95
+ } else {
96
+ if (objectType == Boolean::typeid) return Helpers::ToRubyBoolean((Boolean)object);
97
+ if (objectType == SByte::typeid) return Helpers::ToRubyNumber((SByte)object);
98
+ if (objectType == Int16::typeid) return Helpers::ToRubyNumber((Int16)object);
99
+ if (objectType == Int32::typeid) return Helpers::ToRubyNumber((Int32)object);
100
+ if (objectType == Int64::typeid) return Helpers::ToRubyNumber((Int64)object);
101
+ if (objectType == Byte::typeid) return Helpers::ToRubyNumber((Byte)object);
102
+ if (objectType == UInt16::typeid) return Helpers::ToRubyNumber((UInt16)object);
103
+ if (objectType == UInt32::typeid) return Helpers::ToRubyNumber((UInt32)object);
104
+ if (objectType == UInt64::typeid) return Helpers::ToRubyNumber((UInt64)object);
105
+ if (objectType == String::typeid) return Helpers::ToRubyString((String^)object);
106
+ if (objectType == Single::typeid) return Helpers::ToRubyNumber((Single)object);
107
+ if (objectType == Double::typeid) return Helpers::ToRubyNumber((Double)object);
108
+ }
109
+
110
+ return ruby_value;
111
+ }
112
+
113
+ VALUE ToRubyNumber(Int64 value) { return rb_ll2inum(value); }
114
+ VALUE ToRubyNumber(Int32 value) { return rb_int2inum(value); }
115
+ VALUE ToRubyNumber(Int16 value) { return rb_int2inum(value); }
116
+ VALUE ToRubyNumber(SByte value) { return rb_int2inum(value); }
117
+ VALUE ToRubyNumber(UInt64 value) { return rb_ull2inum(value); }
118
+ VALUE ToRubyNumber(UInt32 value) { return rb_uint2inum(value); }
119
+ VALUE ToRubyNumber(UInt16 value) { return rb_uint2inum(value); }
120
+ VALUE ToRubyNumber(Byte value) { return rb_uint2inum(value); }
121
+ VALUE ToRubyNumber(Double value) { return rb_float_new(value); }
122
+ VALUE ToRubyNumber(Single value) { return rb_float_new(value); }
123
+
124
+ VALUE ToRubyNumber(Decimal value) {
125
+ VALUE val = ToRubyString(value.ToString());
126
+ return rb_funcall(g_CBigDecimal, rb_intern("new"), 1, val);
127
+ }
128
+
129
+ VALUE ToRubyBoolean(bool value) { return value ? Qtrue : Qfalse; }
130
+ VALUE ToRubyNil() { return Qnil; }
131
+
132
+ String^ ToClrString(VALUE self) {
133
+ if (self == Qnil) return nullptr;
134
+ char* native_string = STR2CSTR(self);
135
+
136
+ size_t native_size = strlen(native_string);
137
+ array<unsigned char>^ bytes = gcnew array<unsigned char>(native_size);
138
+
139
+ for(unsigned int i = 0; i < native_size; i++) {
140
+ bytes[i] = System::Byte(native_string[i]);
141
+ }
142
+
143
+ String^ return_value = System::Text::Encoding::UTF8->GetString(bytes);
144
+
145
+ delete bytes;
146
+
147
+ return return_value;
148
+ }
149
+
150
+ VALUE ToRubyString(String^ string) {
151
+ if (string == nullptr) return Qnil;
152
+
153
+ char *ptr = (char*)malloc(4+System::Text::Encoding::UTF8->GetByteCount(string));
154
+ cli::array<unsigned char>^ char_array = System::Text::Encoding::UTF8->GetBytes(string);
155
+ cli::pin_ptr<unsigned char> char_pin_ptr = &char_array[0];
156
+ strcpy(ptr, (char*)char_pin_ptr);
157
+ return rb_str_new2((char*)(void*)ptr);
158
+ }
159
+
160
+ bool ConvertToCharStar( String^ source, char*& target ) {
161
+ int len = (( source->Length+1) * 2);
162
+ target = new char[ len ];
163
+ pin_ptr<const wchar_t> wch = PtrToStringChars( source );
164
+ return wcstombs( target, wch, len ) != -1;
165
+ }
166
+
167
+ void release_object(void *objref) {
168
+ if (objref != 0 && *((int*)objref) != 0) {
169
+ int *objectReference = (int*)objref;
170
+ GCHandle handle = (GCHandle)(IntPtr)*objectReference;
171
+ //Identity::RemoveProxyObject(handle.Target);
172
+ handle.Free();
173
+ xfree(objectReference);
174
+ }
175
+ }
176
+ }
177
+ }
@@ -0,0 +1,44 @@
1
+ #include <windows.h>
2
+ #include <vcclr.h>
3
+ #include <ruby.h>
4
+
5
+ #define _CRT_SECURE_NO_DEPRECATE 1
6
+ #pragma warning(disable:4947)
7
+
8
+ using namespace System;
9
+ using namespace System::Data;
10
+ using namespace System::Data::SqlClient;
11
+ using namespace System::Runtime;
12
+ using namespace System::Runtime::InteropServices;
13
+
14
+ namespace MsSqlClient {
15
+ namespace Helpers {
16
+
17
+ VALUE CastToRubyObject(Type^ clr_type, Object^ object);
18
+ VALUE convert_reader_to_rowset(SqlDataReader^ reader);
19
+
20
+ // Marshaling Methods
21
+ VALUE ToRubyString(String^ string);
22
+ VALUE ToRubyObject(Object^ object);
23
+ VALUE ToRubyNumber(Int64 value);
24
+ VALUE ToRubyNumber(Int32 value);
25
+ VALUE ToRubyNumber(Int16 value);
26
+ VALUE ToRubyNumber(SByte value);
27
+ VALUE ToRubyNumber(UInt64 value);
28
+ VALUE ToRubyNumber(UInt32 value);
29
+ VALUE ToRubyNumber(UInt16 value);
30
+ VALUE ToRubyNumber(Byte value);
31
+ VALUE ToRubyNumber(Double value);
32
+ VALUE ToRubyNumber(Single value);
33
+ VALUE ToRubyNumber(Decimal value);
34
+ VALUE ToRubyBoolean(bool value);
35
+ VALUE ToRubyNil();
36
+ String^ ToClrString(VALUE string);
37
+ VALUE ToRubyString(String^ string);
38
+
39
+ bool ConvertToCharStar( String^ source, char*& target );
40
+
41
+ // Garbage Collection
42
+ void release_object(void *objref);
43
+ }
44
+ }
@@ -0,0 +1,20 @@
1
+ 
2
+ Microsoft Visual Studio Solution File, Format Version 9.00
3
+ # Visual Studio 2005
4
+ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ms_sql_client", "MsSqlClient.express.vcproj", "{358B7FEF-C413-48BA-B3C6-134BCC270245}"
5
+ EndProject
6
+ Global
7
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
8
+ Debug|Win32 = Debug|Win32
9
+ Release|Win32 = Release|Win32
10
+ EndGlobalSection
11
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
12
+ {358B7FEF-C413-48BA-B3C6-134BCC270245}.Debug|Win32.ActiveCfg = Debug|Win32
13
+ {358B7FEF-C413-48BA-B3C6-134BCC270245}.Debug|Win32.Build.0 = Debug|Win32
14
+ {358B7FEF-C413-48BA-B3C6-134BCC270245}.Release|Win32.ActiveCfg = Release|Win32
15
+ {358B7FEF-C413-48BA-B3C6-134BCC270245}.Release|Win32.Build.0 = Release|Win32
16
+ EndGlobalSection
17
+ GlobalSection(SolutionProperties) = preSolution
18
+ HideSolutionNode = FALSE
19
+ EndGlobalSection
20
+ EndGlobal
@@ -0,0 +1,196 @@
1
+ #include "mssqlclient.h"
2
+
3
+ namespace MsSqlClient {
4
+
5
+ VALUE e_ActiveRecordStatementInvalid = rb_eval_string("ActiveRecord::StatementInvalid");
6
+
7
+ gcroot<SqlTransaction^> transaction;
8
+
9
+ // MsSqlAdapter Helper Methods
10
+
11
+ bool in_transaction(VALUE instance) {
12
+ return rb_iv_get(instance, "@in_transaction") == Qtrue;;
13
+ }
14
+
15
+ gcroot<SqlConnection^> get_connection(VALUE instance) {
16
+ if (in_transaction(instance)) {
17
+ return transaction->Connection;
18
+ } else {
19
+ gcroot<SqlConnection^> connection = gcnew SqlConnection();
20
+ connection->ConnectionString = Helpers::ToClrString(rb_funcall(instance, rb_intern("connection_string"), 0));
21
+ connection->Open();
22
+ return connection;
23
+ }
24
+ }
25
+
26
+ void destroy_connection(SqlConnection^ connection) {
27
+ connection->Close();
28
+ delete connection;
29
+ }
30
+
31
+ VALUE c_MsSqlAdapter_begin_db_transaction(VALUE instance) {
32
+ gcroot<SqlConnection^> connection = get_connection(instance);
33
+ transaction = connection->BeginTransaction();
34
+ return rb_iv_set(instance, "@in_transaction", Qtrue);
35
+ }
36
+
37
+ VALUE c_MsSqlAdapter_commit_db_transaction(VALUE instance) {
38
+ gcroot<SqlConnection^> connection = transaction->Connection;
39
+ transaction->Commit();
40
+ destroy_connection(connection);
41
+ delete transaction;
42
+ return rb_iv_set(instance, "@in_transaction", Qfalse);
43
+ }
44
+
45
+ VALUE c_MsSqlAdapter_rollback_db_transaction(VALUE instance) {
46
+ gcroot<SqlConnection^> connection = transaction->Connection;
47
+ transaction->Rollback();
48
+ destroy_connection(connection);
49
+ delete transaction;
50
+ return rb_iv_set(instance, "@in_transaction", Qfalse);
51
+ }
52
+
53
+
54
+ // MsSqlAdapter Instance Methods
55
+
56
+ static VALUE c_MsSqlAdapter_allocate_connection(VALUE self) {
57
+ return Qnil;
58
+ }
59
+
60
+ // This method is not part of the AbstractAdapter implementation
61
+ static VALUE c_MsSqlAdapter_select(VALUE self, VALUE query) {
62
+ try {
63
+ gcroot<SqlConnection^> connection = get_connection(self);
64
+ VALUE records = rb_ary_new();
65
+ gcroot<String^> command_text = Helpers::ToClrString(query);
66
+ gcroot<SqlCommand^> command = connection->CreateCommand();
67
+
68
+ if (in_transaction(self)) {
69
+ command->Transaction = transaction;
70
+ }
71
+
72
+ try {
73
+ command->CommandText = command_text;
74
+ command->CommandType = CommandType::Text;
75
+ gcroot<SqlDataReader^> reader = command->ExecuteReader();
76
+
77
+ if (reader->HasRows) {
78
+ records = Helpers::convert_reader_to_rowset(reader);
79
+ }
80
+
81
+ reader->Close();
82
+ delete reader;
83
+ } catch (SqlException^ sqlEx) {
84
+ char* native_message;
85
+ Helpers::ConvertToCharStar(sqlEx->ToString(), native_message);
86
+ rb_raise(e_ActiveRecordStatementInvalid, native_message);
87
+ command->Cancel();
88
+ } finally {
89
+ delete command;
90
+ delete command_text;
91
+ if (!in_transaction(self)) {
92
+ destroy_connection(connection);
93
+ }
94
+ }
95
+
96
+ return records;
97
+ } catch(Exception^ e) {
98
+ char* native_message;
99
+ Helpers::ConvertToCharStar(e->ToString(), native_message);
100
+ rb_raise(e_ActiveRecordStatementInvalid, native_message);
101
+ }
102
+ }
103
+
104
+ static VALUE c_MsSqlAdapter_insert(VALUE self, VALUE query) {
105
+ gcroot<SqlConnection^> connection = get_connection(self);
106
+ VALUE return_value = Qnil;
107
+ gcroot<String^> command_text = String::Concat(Helpers::ToClrString(query), "\r\nSELECT SCOPE_IDENTITY() as NewIdentity");
108
+ gcroot<SqlCommand^> command;
109
+
110
+ try {
111
+ if (in_transaction(self)) {
112
+ command = transaction->Connection->CreateCommand();
113
+ command->Transaction = transaction;
114
+ } else {
115
+ command = connection->CreateCommand();
116
+ }
117
+
118
+ command->CommandText = command_text;
119
+ command->CommandType = CommandType::Text;
120
+
121
+ gcroot<Object^> value = command->ExecuteScalar();
122
+ return_value = Helpers::ToRubyObject(value);
123
+ } catch (SqlException^ sqlEx) {
124
+ char* native_message;
125
+ Helpers::ConvertToCharStar(String::Format("Command Text: {0}\nError: {1}", command_text, sqlEx->ToString()), native_message);
126
+ rb_raise(e_ActiveRecordStatementInvalid, native_message);
127
+ command->Cancel();
128
+ } finally {
129
+ delete command;
130
+ delete command_text;
131
+
132
+ if (!in_transaction(self)) {
133
+ destroy_connection(connection);
134
+ }
135
+ }
136
+
137
+ return return_value;
138
+
139
+ }
140
+
141
+ static VALUE c_MsSqlAdapter_execute(VALUE self, VALUE query) {
142
+ gcroot<SqlConnection^> connection = get_connection(self);
143
+ VALUE return_value = Qnil;
144
+ gcroot<String^> command_text = Helpers::ToClrString(query);
145
+ gcroot<SqlCommand^> command;
146
+
147
+ try {
148
+
149
+ if (in_transaction(self)) {
150
+ command = transaction->Connection->CreateCommand();
151
+ command->Transaction = transaction;
152
+ } else {
153
+ command = connection->CreateCommand();
154
+ }
155
+
156
+ command->CommandText = command_text;
157
+ command->CommandType = CommandType::Text;
158
+
159
+ return_value = Helpers::ToRubyNumber(command->ExecuteNonQuery());
160
+ } catch (SqlException^ sqlEx) {
161
+ char* native_message;
162
+ Helpers::ConvertToCharStar(String::Format("Command Text: {0}\nError: {1}", command_text, sqlEx->ToString()), native_message);
163
+ rb_raise(e_ActiveRecordStatementInvalid, native_message);
164
+ command->Cancel();
165
+ } finally {
166
+ delete command;
167
+ delete command_text;
168
+
169
+ if (!in_transaction(self)) {
170
+ destroy_connection(connection);
171
+ }
172
+ }
173
+
174
+ return return_value;
175
+ }
176
+
177
+ extern "C" {
178
+
179
+ #pragma unmanaged
180
+ // Main entry point
181
+ __declspec(dllexport) void Init_ms_sql_client() {
182
+ CoInitializeEx(0, COINIT_APARTMENTTHREADED);
183
+
184
+ rb_require("date");
185
+
186
+ VALUE r_MsSqlAdapter_module = rb_define_module("MsSqlClient");
187
+ rb_define_method(r_MsSqlAdapter_module, "_select", RUBY_METHOD_FUNC(c_MsSqlAdapter_select), 1);
188
+ rb_define_method(r_MsSqlAdapter_module, "_insert", RUBY_METHOD_FUNC(c_MsSqlAdapter_insert), 1);
189
+ rb_define_method(r_MsSqlAdapter_module, "_execute", RUBY_METHOD_FUNC(c_MsSqlAdapter_execute), 1);
190
+ rb_define_method(r_MsSqlAdapter_module, "begin_db_transaction", RUBY_METHOD_FUNC(c_MsSqlAdapter_begin_db_transaction), 0);
191
+ rb_define_method(r_MsSqlAdapter_module, "commit_db_transaction", RUBY_METHOD_FUNC(c_MsSqlAdapter_commit_db_transaction), 0);
192
+ rb_define_method(r_MsSqlAdapter_module, "rollback_db_transaction", RUBY_METHOD_FUNC(c_MsSqlAdapter_rollback_db_transaction), 0);
193
+
194
+ }
195
+ }
196
+ }