Class: Extralite::Changeset

Inherits:
Object
  • Object
show all
Defined in:
ext/extralite/changeset.c,
ext/extralite/changeset.c

Overview

This class implements a Changeset for tracking changes to the database.

Instance Method Summary collapse

Constructor Details

#initializevoid

Initializes an empty changeset.



51
52
53
54
55
56
# File 'ext/extralite/changeset.c', line 51

VALUE Changeset_initialize(VALUE self) {
  Changeset_t *changeset = self_to_changeset(self);
  changeset->changeset_len = 0;
  changeset->changeset_ptr = NULL;
  return Qnil;
}

Instance Method Details

#apply(db) ⇒ Extralite::Changeset

Applies the changeset to the given database.

Parameters:

Returns:



351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'ext/extralite/changeset.c', line 351

VALUE Changeset_apply(VALUE self, VALUE db) {
  Changeset_t *changeset = self_to_changeset(self);
  verify_changeset(changeset);

  Database_t *db_struct = self_to_database(db);
  sqlite3 *sqlite3_db = db_struct->sqlite3_db;

  int rc = sqlite3changeset_apply(
    sqlite3_db,
    changeset->changeset_len,
    changeset->changeset_ptr,
    NULL,
    xConflict,
    (void*)1
  );
  if (rc != SQLITE_OK)
    rb_raise(cError, "Error while applying changeset: %s", sqlite3_errstr(rc));

  return self;
}

#eachExtralite::Changeset

Iterates through the changeset, providing each change to the given block. Each change entry is an array containing the operation (:insert / :update / :delete), the table name, an array containing the old values, and an array containing the new values.

changeset.each do |(op, table, old_values, new_values)|
  ...
end

Returns:



308
309
310
311
312
313
314
315
316
317
318
319
# File 'ext/extralite/changeset.c', line 308

VALUE Changeset_each(VALUE self) {
  Changeset_t *changeset = self_to_changeset(self);
  verify_changeset(changeset);

  struct each_ctx ctx = { .iter = NULL };
  int rc = sqlite3changeset_start(&ctx.iter, changeset->changeset_len, changeset->changeset_ptr);
  if (rc!=SQLITE_OK)
    rb_raise(cError, "Error while starting iterator: %s", sqlite3_errstr(rc));

  rb_ensure(SAFE(safe_each), (VALUE)&ctx, SAFE(cleanup_iter), (VALUE)&ctx);
  return self;
}

#invertExtralite::Changeset

Returns an inverted changeset. The inverted changeset can be used to undo the changes in the original changeset.

# undo changes
changeset.invert.apply(db)

Returns:



380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'ext/extralite/changeset.c', line 380

VALUE Changeset_invert(VALUE self) {
  Changeset_t *changeset = self_to_changeset(self);
  verify_changeset(changeset);

  VALUE inverted = rb_funcall(cChangeset, ID_new, 0);
  Changeset_t *inverted_changeset = self_to_changeset(inverted);

  int rc = sqlite3changeset_invert(
    changeset->changeset_len, changeset->changeset_ptr,
    &inverted_changeset->changeset_len, &inverted_changeset->changeset_ptr
  );
  if (rc != SQLITE_OK)
    rb_raise(cError, "Error while inverting changeset: %s", sqlite3_errstr(rc));

  RB_GC_GUARD(inverted);
  return inverted;
}

#load(blob) ⇒ Extralite::Changeset

Loads a changeset from the given string. This method can be used to load a changeset from a file in order to apply it to a database.

changeset = Extralite::Changeset.new
changeset.load(IO.read('my.changes'))
changeset.apply(db)

Parameters:

  • blob (String)

    changeset BLOB

Returns:



424
425
426
427
428
429
430
431
432
433
434
435
436
437
# File 'ext/extralite/changeset.c', line 424

VALUE Changeset_load(VALUE self, VALUE blob) {
  Changeset_t *changeset = self_to_changeset(self);
  if (changeset->changeset_ptr) {
    sqlite3_free(changeset->changeset_ptr);
    changeset->changeset_ptr = NULL;
    changeset->changeset_len = 0;
  }

  changeset->changeset_len = RSTRING_LEN(blob);
  changeset->changeset_ptr = sqlite3_malloc(changeset->changeset_len);
  memcpy(changeset->changeset_ptr, RSTRING_PTR(blob), changeset->changeset_len);

  return self;
}

#to_aArray<Array>

Returns an array containing all changes in the changeset. Each change entry is an array containing the operation (:insert / :update / :delete), the table name, an array containing the old values, and an array containing the new values.

Returns:

  • (Array<Array>)

    changes in the changeset



328
329
330
331
332
333
334
335
336
337
338
# File 'ext/extralite/changeset.c', line 328

VALUE Changeset_to_a(VALUE self) {
  Changeset_t *changeset = self_to_changeset(self);
  verify_changeset(changeset);

  struct each_ctx ctx = { .iter = NULL };
  int rc = sqlite3changeset_start(&ctx.iter, changeset->changeset_len, changeset->changeset_ptr);
  if (rc!=SQLITE_OK)
    rb_raise(cError, "Error while starting iterator: %s", sqlite3_errstr(rc));

  return rb_ensure(SAFE(safe_to_a), (VALUE)&ctx, SAFE(cleanup_iter), (VALUE)&ctx);
}

#to_blobString

Returns a string BLOB containing the changeset in serialized form. The changeset BLOB can be stored to file for later retrieval.

File.open('my.changes', 'w+') { |f| f << changeset.to_blob }

Returns:

  • (String)

    changeset BLOB



405
406
407
408
409
410
411
412
# File 'ext/extralite/changeset.c', line 405

VALUE Changeset_to_blob(VALUE self) {
  Changeset_t *changeset = self_to_changeset(self);

  if (changeset->changeset_ptr)
    return rb_str_new(changeset->changeset_ptr, changeset->changeset_len);
  else
    return rb_str_new("", 0);
}

#track(db, tables) ⇒ Extralite::Changeset

Tracks changes in the given block and collects them into the changeset. Changes are tracked only for the given tables. If nil is supplied as the given tables, changes are tracked for all tables.

# track changes for the foo and bar tables
changeset.track(db, [:foo, :bar]) do
  run_some_queries
end
store_changes(changeset.to_blob)

Parameters:

  • db (Extralite::Database)

    database to track

  • tables (Array<String, Symbol>, nil)

    tables to track (or nil for all tables)

Returns:



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'ext/extralite/changeset.c', line 131

VALUE Changeset_track(VALUE self, VALUE db, VALUE tables) {
  Changeset_t *changeset = self_to_changeset(self);
  Database_t *db_struct = self_to_database(db);
  sqlite3 *sqlite3_db = db_struct->sqlite3_db;

  if (changeset->changeset_ptr) {
    sqlite3_free(changeset->changeset_ptr);
    changeset->changeset_len = 0;
    changeset->changeset_ptr = NULL;
  }

  struct track_ctx ctx = {
    .changeset = changeset,
    .sqlite3_db = sqlite3_db,
    .session = NULL,
    .db = db,
    .tables = tables
  };
  int rc = sqlite3session_create(sqlite3_db, "main", &ctx.session);
  if (rc != SQLITE_OK)
    rb_raise(cError, "Error while creating session: %s", sqlite3_errstr(rc));

  rb_ensure(SAFE(safe_track), (VALUE)&ctx, SAFE(cleanup_track), (VALUE)&ctx);

  return self;
}