intersys 0.1 → 0.2

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,132 @@
1
+ # Author/Maintainer: Max Lapshin <max@maxidoors.ru>
2
+
3
+ require 'rubygems'
4
+ require_gem 'activerecord'
5
+
6
+ require 'active_record/connection_adapters/abstract_adapter'
7
+
8
+ module ActiveRecord
9
+ class Base
10
+ def self.intersys_connection(config)
11
+ config = config.symbolize_keys
12
+ require 'intersys'
13
+ intersys = Intersys::Database.new(config)
14
+ ConnectionAdapters::IntersysAdapter.new(intersys, logger, config)
15
+ end
16
+ end
17
+
18
+ module ConnectionAdapters
19
+ class IntersysAdapter < AbstractAdapter
20
+ attr_reader :config
21
+ def initialize(database, logger, config)
22
+ super(database, logger)
23
+ @config = config
24
+ end
25
+
26
+ def adapter_name #:nodoc:
27
+ 'Intersys Caché'
28
+ end
29
+
30
+ def supports_migrations? #:nodoc:
31
+ false
32
+ end
33
+
34
+ def active?
35
+ warn "IntersysAdapter#active? not implemented"
36
+ return true
37
+ @connection.query "select 1"
38
+ rescue Intersys::IntersysException
39
+ false
40
+ end
41
+
42
+ def disconnect!
43
+ @connection.close! rescue nil
44
+ end
45
+
46
+ def reconnect!
47
+ disconnect!
48
+ connect
49
+ end
50
+
51
+ def begin_db_transaction
52
+ @connection.start
53
+ end
54
+ def commit_db_transaction
55
+ @connection.commit
56
+ end
57
+ def rollback_db_transaction
58
+ @connection.rollback
59
+ end
60
+
61
+ def add_limit_offset!(sql, options)
62
+ sql << " LIMIT #{limit}" if limit = options[:limit]
63
+ sql << " OFFSET #{offset}" if offset = options[:offset]
64
+ end
65
+
66
+ def select_all(sql, name = nil) #:nodoc:
67
+ select(sql, name)
68
+ end
69
+
70
+ def select_one(sql, name = nil) #:nodoc:
71
+ result = select(sql, name)
72
+ result.nil? ? nil : result.first
73
+ end
74
+
75
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
76
+ execute(sql, name = nil)
77
+ id_value || @connection.insert_id
78
+ end
79
+
80
+ def update(sql, name = nil) #:nodoc:
81
+ execute(sql, name)
82
+ @connection.affected_rows
83
+ end
84
+
85
+ alias_method :delete, :update #:nodoc:
86
+
87
+
88
+
89
+ def columns(table_name, name = nil)
90
+ @cdef = Intersys::Reflection::ClassDefinition.open("User.#{table_name.camelize}")
91
+ result = [IntersysColumn.new("id", "", "int")]
92
+ @cdef.properties.each do |prop|
93
+ result << IntersysColumn.new(prop.sql_field_name, prop.initial_expression, prop.Type.gsub("%","").underscore, prop.required)
94
+ end
95
+ result
96
+ end
97
+
98
+ def native_database_types
99
+ {
100
+ :primary_key => 'int generated by default as identity (start with 42) primary key',
101
+ :string => { :name => 'varchar', :limit => 255 },
102
+ :text => { :name => 'clob', :limit => 32768 },
103
+ :integer => { :name => 'int' },
104
+ :float => { :name => 'float' },
105
+ :datetime => { :name => 'timestamp' },
106
+ :timestamp => { :name => 'timestamp' },
107
+ :time => { :name => 'time' },
108
+ :date => { :name => 'date' },
109
+ :binary => { :name => 'blob', :limit => 32768 },
110
+ :boolean => { :name => 'decimal', :limit => 1 }
111
+ }
112
+ end
113
+
114
+ protected
115
+ # :nodoc
116
+ def connect
117
+ @connection = Intersys::Database.new(config)
118
+ end
119
+
120
+ def select(sql, name = nil)
121
+ @connection.query(sql)
122
+ end
123
+
124
+ def execute(sql, name = nil)
125
+ @connection.execute(sql)
126
+ end
127
+ end
128
+
129
+ class IntersysColumn < Column
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,163 @@
1
+ module Intersys
2
+ # Basic class for all classes, through which is performed access to Cache classes
3
+ # For each Cache class must be created ruby class, inherited from Intersys::Object
4
+ #
5
+ # By default prefix "User" is selected. If Cache class has another prefix, it must
6
+ # be provided explicitly via method "prefix":
7
+ # class List < Intersys::Object
8
+ # prefix "%Library"
9
+ # end
10
+ #
11
+ # By default name of Cache class is taken the same as name of ruby class.
12
+ # Thus in this example this class List will be marshalled to Cache class
13
+ # %Library.List
14
+ #
15
+ class Object
16
+
17
+ class << self
18
+ protected
19
+ def class_names
20
+ common_get_or_set("@class_names", {})
21
+ end
22
+
23
+ def prefix=(name)
24
+ @prefix = name
25
+ @class_name = @prefix + "." + (@class_name ? @class_name.split(".").last : self.to_s)
26
+ register_name!
27
+ end
28
+
29
+ def class_name=(name)
30
+ if name.index(".")
31
+ self.prefix = name.split(".").first
32
+ @class_name = name
33
+ else
34
+ @class_name = self.prefix + "." + name
35
+ end
36
+ register_name!
37
+ end
38
+
39
+ # Register class name of current class in global list
40
+ def register_name!
41
+ if i = class_names.index(self)
42
+ class_names.delete(i)
43
+ end
44
+ class_names[class_name] = self
45
+ class_name
46
+ end
47
+
48
+ public
49
+ # Nice function, that generates description of Cache class, looking just as C++ one
50
+ # Maybe, later, it will be possible even to generate IDL, using this code
51
+ def intersys_description
52
+ "class #{class_name} { \n" + intersys_reflector.all_methods.map do |mtd|
53
+ begin
54
+ "\t"+intersys_method(mtd).description+";\n"
55
+ rescue
56
+ "\tundefined #{mtd}\n"
57
+ end
58
+ end.join("") + "};"
59
+ end
60
+
61
+ # Help to work with instance variables of Intersys::Object class
62
+ # required, because list of registered descendants of Intersys::Object,
63
+ # database connection etc. should be in one place
64
+ def common_get_or_set(name, default_value = nil)
65
+ unless var = Intersys::Object.instance_variable_get(name)
66
+ default = block_given? ? yield : default_value
67
+ var = Intersys::Object.instance_variable_set(name, default)
68
+ end
69
+ var
70
+ end
71
+
72
+ # Takes Cache class name and try to resolve it to Ruby class
73
+ def lookup(class_name)
74
+ class_names[class_name] || raise(UnMarshallError, "Couldn't find registered class with Cache name '#{class_name}'")
75
+ end
76
+
77
+ # Each Cache class has prefix before it's name: namespace.
78
+ # this method set's prefix for current class is provided,
79
+ # or just returns current prefix
80
+ def prefix(name = nil)
81
+ self.prefix = name if name
82
+ @prefix ||= "User"
83
+ end
84
+
85
+ # Returns Cache class name, if called without parameters, or sets one, if passed
86
+ def class_name(name = nil)
87
+ self.class_name = name if name
88
+ self.class_name = (prefix + "." + to_s) unless @class_name
89
+ @class_name
90
+ end
91
+
92
+ # Returns database, if called without parameters, or sets one, if passed
93
+ # Once established, it is not possible now to connect to another database
94
+ def database(db_options = {})
95
+ common_get_or_set("@database") do
96
+ Intersys::Database.new({:user => "_SYSTEM", :password => "SYS", :namespace => "User"}.merge(db_options))
97
+ end
98
+ end
99
+
100
+ # This method takes block and executes it between
101
+ # START TRANSACTION and COMMIT TRANSACTION
102
+ #
103
+ # In case of exception ROLLBACK TRANSACTION is called
104
+ def transaction
105
+ return unless block_given?
106
+ database.start
107
+ begin
108
+ yield
109
+ database.commit
110
+ rescue StandardError => e
111
+ database.rollback
112
+ raise e
113
+ end
114
+ end
115
+
116
+ # :nodoc
117
+ def inherited(klass)
118
+ class_names[klass.class_name] = klass
119
+ end
120
+
121
+ # Look into Cache documentation for what is concurrency. I don't know
122
+ def concurrency
123
+ 1
124
+ end
125
+
126
+ # timeout for connection
127
+ def timeout
128
+ 5
129
+ end
130
+
131
+ # Nice method, that deletes all instances of class.
132
+ # You can just Person.delete_extent, but Person.delete_all looks more like ActiveRecord
133
+ def delete_all
134
+ intersys_call("%DeleteExtent")
135
+ end
136
+
137
+ end
138
+
139
+ # You can ask for database from instance
140
+ def database
141
+ self.class.database
142
+ end
143
+
144
+ # You can ask from instance it's Cache class name
145
+ def class_name
146
+ self.class.class_name
147
+ end
148
+
149
+ # Returns id of current object.
150
+ # You can remove this method and You will get string ID, so leave it here
151
+ # However, if You ask reflector for id, it will give incorrect answer,
152
+ # because Cache allows id to be string
153
+ def id
154
+ intersys_call("%Id").to_i
155
+ end
156
+
157
+ # Destroys current object
158
+ def destroy
159
+ self.class.intersys_call("%DeleteId", id)
160
+ end
161
+
162
+ end
163
+ end
@@ -1,4 +1,6 @@
1
1
  #include "intersys.h"
2
+ #ifdef HAVE_SQL_H
3
+
2
4
  #ifdef __CYGWIN__
3
5
  #include <windef.h>
4
6
  #endif
@@ -31,8 +33,97 @@ VALUE intersys_query_initialize(VALUE self, VALUE database, VALUE sql_query) {
31
33
  int sql_code;
32
34
  Data_Get_Struct(self, struct rbQuery, query);
33
35
  Data_Get_Struct(database, struct rbDatabase, base);
36
+ rb_iv_set(self, "@database", database);
37
+ query->limit = -1;
34
38
  RUN(cbind_alloc_query(base->database, &query->query));
35
- RUN(cbind_prepare_gen_query(query->query, WCHARSTR(sql_query), &sql_code));
39
+ RUN(cbind_prepare_gen_query(query->query, WCHARSTR(TOWCHAR(sql_query)), &sql_code));
40
+ return self;
41
+ }
42
+
43
+ static void query_bind_one_param(h_query query, int index, VALUE obj) {
44
+ int sql_type;
45
+ RUN(cbind_query_get_par_sql_type(query, index, &sql_type));
46
+
47
+ switch (sql_type) {
48
+ case SQL_CHAR:
49
+ case SQL_VARCHAR:
50
+ case SQL_LONGVARCHAR: {
51
+ VALUE str = rb_funcall(obj, rb_intern("to_s"), 0);
52
+ RUN(cbind_query_set_mb_str_par(query, index, STR(str), LEN(str)));
53
+ break;
54
+ }
55
+ case SQL_BINARY:
56
+ case SQL_LONGVARBINARY:
57
+ case SQL_VARBINARY: {
58
+ VALUE str = rb_funcall(obj, rb_intern("to_s"), 0);
59
+ RUN(cbind_query_set_bin_par(query, index, STR(str), LEN(str)));
60
+ break;
61
+ }
62
+ case SQL_TINYINT:
63
+ case SQL_SMALLINT:
64
+ case SQL_INTEGER:
65
+ case SQL_BIGINT:
66
+ case SQL_BIT:
67
+ {
68
+ VALUE num = rb_funcall(obj, rb_intern("to_i"), 0);
69
+ RUN(cbind_query_set_int_par(query, index, NUM2INT(num)));
70
+ break;
71
+ }
72
+ case SQL_FLOAT:
73
+ case SQL_DOUBLE:
74
+ case SQL_REAL:
75
+ case SQL_NUMERIC:
76
+ case SQL_DECIMAL:
77
+ {
78
+ VALUE f = rb_funcall(obj, rb_intern("to_f"), 0);
79
+ RUN(cbind_query_set_double_par(query, index, RFLOAT(f)->value));
80
+ break;
81
+ }
82
+ case SQL_TIME:
83
+ {
84
+ int hour = NUM2INT(CALL(obj, "hour"));
85
+ int minute = NUM2INT(CALL(obj, "min"));
86
+ int second = NUM2INT(CALL(obj, "sec"));
87
+ RUN(cbind_query_set_time_par(query, index, hour, minute, second));
88
+ break;
89
+ }
90
+ case SQL_DATE:
91
+ {
92
+ int year = NUM2INT(CALL(obj, "year"));
93
+ int month = NUM2INT(CALL(obj, "month"));
94
+ int day = NUM2INT(CALL(obj, "day"));
95
+ RUN(cbind_query_set_date_par(query, index, year, month, day));
96
+ break;
97
+ }
98
+ case SQL_TIMESTAMP:
99
+ {
100
+ int year = NUM2INT(CALL(obj, "year"));
101
+ int month = NUM2INT(CALL(obj, "month"));
102
+ int day = NUM2INT(CALL(obj, "day"));
103
+ int hour = NUM2INT(CALL(obj, "hour"));
104
+ int minute = NUM2INT(CALL(obj, "min"));
105
+ int second = NUM2INT(CALL(obj, "sec"));
106
+ int fraction = 0;
107
+ RUN(cbind_query_set_timestamp_par(query, index,
108
+ year, month, day, hour, minute, second, fraction));
109
+ break;
110
+ }
111
+
112
+ default:
113
+ rb_raise(cMarshallError, "unknown sql type %d for parameter N %d", sql_type, index);
114
+ }
115
+ }
116
+
117
+
118
+ VALUE intersys_query_bind_params(VALUE self, VALUE params) {
119
+ int i;
120
+ struct rbQuery* query;
121
+ Check_Type(params, T_ARRAY);
122
+ Data_Get_Struct(self, struct rbQuery, query);
123
+
124
+ for(i = 0; i < RARRAY(params)->len; i++) {
125
+ query_bind_one_param(query->query, i, RARRAY(params)->ptr[i]);
126
+ }
36
127
  return self;
37
128
  }
38
129
 
@@ -47,6 +138,7 @@ VALUE intersys_query_execute(VALUE self) {
47
138
  return self;
48
139
  }
49
140
 
141
+
50
142
  VALUE intersys_query_get_data(VALUE self, VALUE index) {
51
143
  struct rbQuery* query;
52
144
  int type = 0;
@@ -129,7 +221,7 @@ VALUE intersys_query_column_name(VALUE self, VALUE i) {
129
221
 
130
222
  VALUE intersys_query_fetch(VALUE self) {
131
223
  struct rbQuery* query;
132
- VALUE data;
224
+ VALUE data = Qnil;
133
225
  Data_Get_Struct(self, struct rbQuery, query);
134
226
  int num_cols = 0;
135
227
  int i = 0;
@@ -153,13 +245,61 @@ VALUE intersys_query_fetch(VALUE self) {
153
245
  for(i = 0; i < num_cols; i++) {
154
246
  rb_ary_push(data, rb_funcall(self, rb_intern("get_data"), 1, INT2FIX(i+1)));
155
247
  }
156
- rb_funcall(self, rb_intern("close"), 0);
157
248
  return data;
158
249
  }
159
250
 
251
+
252
+ VALUE intersys_query_each(VALUE self) {
253
+ struct rbQuery* query;
254
+ int i;
255
+ Data_Get_Struct(self, struct rbQuery, query);
256
+ if(query->offset > 0) {
257
+ RUN(cbind_query_skip(query->query, query->offset));
258
+ }
259
+ for(i = query->offset; i < query->offset + query->limit; i++) {
260
+ VALUE row = intersys_query_fetch(self);
261
+ if(row == Qnil || RARRAY(row)->len == 0) {
262
+ break;
263
+ }
264
+ rb_yield(row);
265
+ }
266
+ query_close(query);
267
+ return self;
268
+ }
269
+
270
+
271
+
160
272
  VALUE intersys_query_close(VALUE self) {
161
273
  struct rbQuery* query;
162
274
  Data_Get_Struct(self, struct rbQuery, query);
163
275
  query_close(query);
164
276
  return self;
165
277
  }
278
+
279
+
280
+ VALUE intersys_query_set_limit(VALUE self, VALUE limit) {
281
+ struct rbQuery* query;
282
+ Data_Get_Struct(self, struct rbQuery, query);
283
+ query->limit = NUM2INT(rb_funcall(limit, rb_intern("to_i"), 0));
284
+ return limit;
285
+ }
286
+ VALUE intersys_query_get_limit(VALUE self) {
287
+ struct rbQuery* query;
288
+ Data_Get_Struct(self, struct rbQuery, query);
289
+ return INT2FIX(query->limit);
290
+ }
291
+
292
+ VALUE intersys_query_set_offset(VALUE self, VALUE offset) {
293
+ struct rbQuery* query;
294
+ Data_Get_Struct(self, struct rbQuery, query);
295
+ query->offset = NUM2INT(rb_funcall(offset, rb_intern("to_i"), 0));
296
+ return offset;
297
+ }
298
+
299
+ VALUE intersys_query_get_offset(VALUE self) {
300
+ struct rbQuery* query;
301
+ Data_Get_Struct(self, struct rbQuery, query);
302
+ return INT2FIX(query->offset);
303
+ }
304
+
305
+ #endif /* HAVE_SQL_H */