Class: Swift::Adapter

Inherits:
Object
  • Object
show all
Defined in:
lib/swift/identity_map.rb,
lib/swift/adapter.rb,
ext/adapter.cc

Overview

IdentityMap

Direct Known Subclasses

DB::DB2, DB::Mysql, DB::Postgres

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'ext/adapter.cc', line 105

static VALUE adapter_initialize(VALUE self, VALUE options) {
  VALUE db       = rb_hash_aref(options, ID2SYM(rb_intern("db")));
  VALUE driver   = rb_hash_aref(options, ID2SYM(rb_intern("driver")));
  VALUE user     = rb_hash_aref(options, ID2SYM(rb_intern("user")));

  if (NIL_P(db))     rb_raise(eSwiftArgumentError, "Adapter#new called without :db");
  if (NIL_P(driver)) rb_raise(eSwiftArgumentError, "Adapter#new called without :driver");

  user = NIL_P(user) ? rb_str_new2(getlogin()) : user;

  try {
    DATA_PTR(self) = new dbi::Handle(
      CSTRING(driver),
      CSTRING(user),
      CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("password")))),
      CSTRING(db),
      CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("host")))),
      CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("port"))))
    );
  }
  CATCH_DBI_EXCEPTIONS();

  rb_iv_set(self, "@timezone", rb_hash_aref(options, ID2SYM(rb_intern("timezone"))));
  rb_iv_set(self, "@options",  options);
  return Qnil;
}

Instance Attribute Details

#optionsObject (readonly)

Returns the value of attribute options.



3
4
5
# File 'lib/swift/adapter.rb', line 3

def options
  @options
end

Instance Method Details

#all(scheme, conditions = '', *binds, &block) ⇒ Object



10
11
12
13
# File 'lib/swift/adapter.rb', line 10

def all scheme, conditions = '', *binds, &block
  where = "where #{exchange_names(scheme, conditions)}" unless conditions.empty?
  prepare(scheme, "select * from #{scheme.store} #{where}").execute(*binds, &block)
end

#begin(*args) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
# File 'ext/adapter.cc', line 25

static VALUE adapter_begin(int argc, VALUE *argv, VALUE self) {
  VALUE save_point;
  rb_scan_args(argc, argv, "01", &save_point);

  dbi::Handle *handle = adapter_handle(self);
  try {
    NIL_P(save_point) ? handle->begin() : handle->begin(CSTRING(save_point));
  }
  CATCH_DBI_EXCEPTIONS();
  return Qtrue;
}

#cloneObject

TODO:



44
45
46
# File 'ext/adapter.cc', line 44

static VALUE adapter_clone(VALUE self) {
  rb_raise(eSwiftRuntimeError, "clone is not allowed.");
}

#closeObject



37
38
39
40
41
# File 'ext/adapter.cc', line 37

static VALUE adapter_close(VALUE self) {
  dbi::Handle *handle = adapter_handle(self);
  try { handle->close(); } CATCH_DBI_EXCEPTIONS();
  return Qtrue;
}

#commit(*args) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
# File 'ext/adapter.cc', line 48

static VALUE adapter_commit(int argc, VALUE *argv, VALUE self) {
  VALUE save_point;
  rb_scan_args(argc, argv, "01", &save_point);
  dbi::Handle *handle = adapter_handle(self);

  try {
    NIL_P(save_point) ? handle->commit() : handle->commit(CSTRING(save_point));
  }
  CATCH_DBI_EXCEPTIONS();
  return Qtrue;
}

#create(scheme, *relations) ⇒ Object



20
21
22
23
24
25
26
27
28
29
# File 'lib/swift/adapter.rb', line 20

def create scheme, *relations
  statement = prepare_create(scheme)
  relations.map do |relation|
    relation = scheme.new(relation) unless relation.kind_of?(scheme)
    if statement.execute(*relation.tuple.values_at(*scheme.header.insertable)) && scheme.header.serial
      relation.tuple[scheme.header.serial] = statement.insert_id
    end
    relation
  end
end

#destroy(scheme, *relations) ⇒ Object



40
41
42
43
44
45
46
47
48
49
# File 'lib/swift/adapter.rb', line 40

def destroy scheme, *relations
  statement = prepare_destroy(scheme)
  relations.map do |relation|
    relation = scheme.new(relation) unless relation.kind_of?(scheme)
    if result = statement.execute(*relation.tuple.values_at(*scheme.header.keys))
      relation.freeze
    end
    result
  end
end

#drop_store(name) ⇒ Object



60
61
62
# File 'lib/swift/adapter.rb', line 60

def drop_store name
  execute("drop table if exists #{name}")
end

#dupObject

TODO:



61
62
63
# File 'ext/adapter.cc', line 61

static VALUE adapter_dup(VALUE self) {
  rb_raise(eSwiftRuntimeError, "dup is not allowed.");
}

#escape(value) ⇒ Object

TODO: Attempt TO_S() before escaping?



66
67
68
69
70
71
72
73
74
75
# File 'ext/adapter.cc', line 66

static VALUE adapter_escape(VALUE self, VALUE value) {
  if (TYPE(value) != T_STRING) rb_raise(eSwiftArgumentError, "Cannot escape non-string value.");

  dbi::Handle *handle = adapter_handle(self);
  try {
    std::string safe = handle->escape(std::string(RSTRING_PTR(value), RSTRING_LEN(value)));
    return rb_str_new(safe.data(), safe.length());
  }
  CATCH_DBI_EXCEPTIONS();
}

#execute(*args) ⇒ Object

TODO: Change bind_values to an array in the interface? Avoid array -> splat -> array.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'ext/adapter.cc', line 78

static VALUE adapter_execute(int argc, VALUE *argv, VALUE self) {
  VALUE statement, bind_values, block, rows;

  dbi::Handle *handle = adapter_handle(self);
  rb_scan_args(argc, argv, "1*&", &statement, &bind_values, &block);

  try {
    Query query;
    query.sql    = CSTRING(statement);
    query.handle = handle;

    if (RARRAY_LEN(bind_values) > 0) query_bind_values(&query, bind_values, handle->driver());
    if (dbi::_trace)                 dbi::logMessage(dbi::_trace_fd, dbi::formatParams(query.sql, query.bind));

    if ((rows = rb_thread_blocking_region(((VALUE (*)(void*))query_execute), &query, RUBY_UBF_IO, 0)) == Qfalse)
      rb_raise(eSwiftRuntimeError, "%s", query.error);

    if (rb_block_given_p()) {
      dbi::AbstractResult *result = handle->results();
      return result_each(result_wrap_handle(cSwiftResult, self, result, false));
    }
    else
      return rows;
  }
  CATCH_DBI_EXCEPTIONS();
}

#first(scheme, conditions = '', *binds, &block) ⇒ Object



15
16
17
18
# File 'lib/swift/adapter.rb', line 15

def first scheme, conditions = '', *binds, &block
  where = "where #{exchange_names(scheme, conditions)}" unless conditions.empty?
  prepare(scheme, "select * from #{scheme.store} #{where} limit 1").execute(*binds, &block).first
end

#get(scheme, keys) ⇒ Object



5
6
7
8
# File 'lib/swift/adapter.rb', line 5

def get scheme, keys
  relation = scheme.new(keys)
  prepare_get(scheme).execute(*relation.tuple.values_at(*scheme.header.keys)).first
end

#identity_mapObject



31
32
33
# File 'lib/swift/identity_map.rb', line 31

def identity_map
  @identity_map ||= IdentityMap.new
end

#migrate!(scheme) ⇒ Object



51
52
53
54
55
56
57
58
# File 'lib/swift/adapter.rb', line 51

def migrate! scheme
  keys   =  scheme.header.keys
  fields =  scheme.header.map{|p| field_definition(p)}.join(', ')
  fields += ", primary key (#{keys.join(', ')})" unless keys.empty?

  drop_store scheme.store
  execute("create table #{scheme.store} (#{fields})")
end

#prepare(*args) ⇒ Object



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'ext/adapter.cc', line 132

static VALUE adapter_prepare(int argc, VALUE *argv, VALUE self) {
  VALUE sql, scheme, prepared;
  dbi::AbstractStatement *statement;

  rb_scan_args(argc, argv, "11", &scheme, &sql);
  if (TYPE(scheme) != T_CLASS) {
    sql    = scheme;
    scheme = Qnil;
  }

  dbi::Handle *handle = adapter_handle(self);
  try {
    // TODO: Move to statement_* constructor.
    statement = handle->conn()->prepare(CSTRING(sql));
    prepared  = statement_wrap_handle(cSwiftStatement, self, statement);
    rb_iv_set(prepared, "@scheme",  scheme);
    return prepared;
  }
  CATCH_DBI_EXCEPTIONS();
}

#resultsObject

TODO Figure out how to avoid race conditions.



230
231
232
233
234
235
236
237
# File 'ext/adapter.cc', line 230

VALUE adapter_results(VALUE self) {
  dbi::Handle *handle = adapter_handle(self);
  try {
    dbi::AbstractResult *result = handle->results();
    return result_wrap_handle(cSwiftResult, self, result, false);
  }
  CATCH_DBI_EXCEPTIONS();
}

#rollback(*args) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
# File 'ext/adapter.cc', line 153

static VALUE adapter_rollback(int argc, VALUE *argv, VALUE self) {
  VALUE save_point;
  dbi::Handle *handle = adapter_handle(self);
  rb_scan_args(argc, argv, "01", &save_point);

  try {
    NIL_P(save_point) ? handle->rollback() : handle->rollback(CSTRING(save_point));
  }
  CATCH_DBI_EXCEPTIONS();
  return Qtrue;
}

#transaction(*args) ⇒ Object



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'ext/adapter.cc', line 165

static VALUE adapter_transaction(int argc, VALUE *argv, VALUE self) {
  int status;
  VALUE sp, block;

  dbi::Handle *handle = adapter_handle(self);

  rb_scan_args(argc, argv, "01&", &sp, &block);

  if (NIL_P(block)) rb_raise(eSwiftArgumentError, "Transaction called without a block.");
  std::string save_point = NIL_P(sp) ? "SP" + dbi::generateCompactUUID() : CSTRING(sp);

  try {
    handle->begin(save_point);
    rb_protect(rb_yield, self, &status);
    if (!status && handle->transactions().size() > 0) {
      handle->commit(save_point);
    }
    else if (status && handle->transactions().size() > 0) {
      handle->rollback(save_point);
      rb_jump_tag(status);
    }
  }
  CATCH_DBI_EXCEPTIONS();

  return Qtrue;
}

#update(scheme, *relations) ⇒ Object



31
32
33
34
35
36
37
38
# File 'lib/swift/adapter.rb', line 31

def update scheme, *relations
  statement = prepare_update(scheme)
  relations.map do |relation|
    relation = scheme.new(relation) unless relation.kind_of?(scheme)
    statement.execute(*relation.tuple.values_at(*scheme.header.updatable, *scheme.header.keys))
    relation
  end
end

#write(*args) ⇒ Object



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'ext/adapter.cc', line 192

static VALUE adapter_write(int argc, VALUE *argv, VALUE self) {
  uint64_t rows = 0;
  VALUE stream, table, fields;
  dbi::Handle *handle = adapter_handle(self);

  rb_scan_args(argc, argv, "30", &table, &fields, &stream);
  if (TYPE(stream) != T_STRING && !rb_respond_to(stream, rb_intern("read")))
    rb_raise(eSwiftArgumentError, "Stream must be a String or IO object.");
  if (TYPE(fields) != T_ARRAY)
    rb_raise(eSwiftArgumentError, "Fields must be an Array.");

  try {
    dbi::FieldSet write_fields;
    for (int i = 0; i < RARRAY_LEN(fields); i++) {
      VALUE field = TO_S(rb_ary_entry(fields, i));
      write_fields << std::string(RSTRING_PTR(field), RSTRING_LEN(field));
    }

    /*
      TODO: Adapter specific code is balls.
      This is just for the friggin mysql support - mysql does not like a statement close command being send on a
      handle when the writing has started.
    */
    rb_gc();

    if (TYPE(stream) == T_STRING) {
      dbi::IOStream io(RSTRING_PTR(stream), RSTRING_LEN(stream));
      rows = handle->write(RSTRING_PTR(table), write_fields, &io);
    }
    else {
      IOStream io(stream);
      rows = handle->write(RSTRING_PTR(table), write_fields, &io);
    }
    return SIZET2NUM(rows);
  }
  CATCH_DBI_EXCEPTIONS();
}