ext/Row.c in rubyfb-0.6.3 vs ext/Row.c in rubyfb-0.6.4

- old
+ new

@@ -23,17 +23,17 @@ * @version 1.0 */ /* Includes. */ #include "Row.h" +#include "TypeMap.h" #include "FireRuby.h" #include "ibase.h" #include "ruby.h" /* Function prototypes. */ -static VALUE allocateRow(VALUE); -static VALUE initializeRow(VALUE, VALUE, VALUE, VALUE); +static VALUE initializeRow(VALUE, VALUE, VALUE); static VALUE columnsInRow(VALUE); static VALUE getRowNumber(VALUE); static VALUE getColumnName(VALUE, VALUE); static VALUE getColumnAlias(VALUE, VALUE); static VALUE getColumnScale(VALUE, VALUE); @@ -55,77 +55,76 @@ static VALUE rowToHash(VALUE); static VALUE rowValuesAt(int, VALUE *, VALUE); /* Globals. */ VALUE cRow; +ID EQL_ID, FETCH_ID, NEW_ID, + AT_COLUMNS_ID, AT_NUMBER_ID, + AT_NAME_ID, AT_ALIAS_ID, AT_KEY_ID, AT_SCALE_ID, + AT_VALUE_ID, AT_TYPE_ID, AT_COLUMN_ID; -/** - * This function integrates with the Ruby garbage collector to insure that - * all resources associated with a Row object are marked during the mark phase - * - * @param row A pointer to the RowHandle object for the Row object. - * - */ -void rowGCMark(void *handle) { - if(handle != NULL) { - RowHandle *row = (RowHandle *)handle; - int i; - for(i = 0; i < row->size; i++) { - rb_gc_mark(row->columns[i].value); - rb_gc_mark(row->columns[i].type); - rb_gc_mark(row->columns[i].scale); - } +static VALUE getColumns(VALUE self) { + return rb_ivar_get(self, AT_COLUMNS_ID); +} + +static VALUE checkRowOffset(VALUE columns, VALUE index) { + int offset = FIX2INT(index); + + /* Correct negative index values. */ + if(offset < 0) { + return INT2NUM(RARRAY_LEN(columns) + offset); } + return index; } -/** - * This function integrates with the Ruby garbage collector to insure that - * all resources associated with a Row object are released whenever the Row - * object is collected. - * - * @param row A pointer to the RowHandle object for the Row object. - * - */ -void freeRow(void *row) { - if(row != NULL) { - RowHandle *handle = (RowHandle *)row; +static VALUE metadataScan(VALUE self, VALUE key, ID slot) { + long idx; + VALUE columns = getColumns(self); - if(handle->columns != NULL) { - free(handle->columns); + for(idx=0; idx < RARRAY_LEN(columns); idx++) { + VALUE column = rb_ary_entry(columns, idx), + meta_data = rb_ivar_get(column, AT_COLUMN_ID); + if(Qtrue == rb_funcall(rb_ivar_get(meta_data, slot), EQL_ID, 1, key)) { + return(column); } - free(handle); } + return(Qnil); } -/** - * This function integrates with the Ruby memory allocation system to allocate - * space for new Row objects. - * - * @param klass A reference to the Row class. - * - * @return A reference to the Row class instance allocated. - * - */ -static VALUE allocateRow(VALUE klass) { - VALUE row; - RowHandle *handle = ALLOC(RowHandle); - - if(handle != NULL) { - /* Initialise the row fields. */ - handle->size = 0; - handle->number = 0; - handle->columns = NULL; - row = Data_Wrap_Struct(klass, rowGCMark, freeRow, handle); - } else { - /* Generate an exception. */ - rb_raise(rb_eNoMemError, "Memory allocation failure allocating a row."); +static VALUE rowCollect(VALUE self, ID slot, short metadata_flag) { + VALUE columns = getColumns(self); + long idx, size = RARRAY_LEN(columns); + VALUE result = rb_ary_new2(size); + for(idx=0; idx < size; idx++) { + VALUE target = rb_ary_entry(columns, idx); + if(metadata_flag) { + target = rb_ivar_get(target, AT_COLUMN_ID); + } + rb_ary_store(result, idx, rb_ivar_get(target, slot)); } + return(result); +} - return(row); +static VALUE getColumn(VALUE columns, VALUE index) { + return rb_funcall(columns, FETCH_ID, 1, index); } +static VALUE getMetadata(VALUE columns, VALUE index) { + return rb_ivar_get(getColumn(columns, index), AT_COLUMN_ID); +} +static VALUE columnKey(VALUE column) { + return rb_ivar_get(rb_ivar_get(column, AT_COLUMN_ID), AT_KEY_ID); +} + +static VALUE keyValuePair(VALUE column) { + VALUE result = rb_ary_new2(2); + rb_ary_store(result, 0, columnKey(column)); + rb_ary_store(result, 1, rb_ivar_get(column, AT_VALUE_ID)); + return(result); +} + /** * This function provides the initialize method for the Row class. * * @param self A reference to the Row object to be initialized. * @param results A reference to the ResultSet object that generated the row @@ -138,50 +137,13 @@ * row. * * @return A reference to the initialize Row object. * */ -static VALUE initializeRow(VALUE self, VALUE results, VALUE data, VALUE number) { - RowHandle *row = NULL; - VALUE value = rb_funcall(data, rb_intern("size"), 0); - - Data_Get_Struct(self, RowHandle, row); - rb_iv_set(self, "@number", number); - row->size = TYPE(value) == T_FIXNUM ? FIX2INT(value) : NUM2INT(value); - if(row->size > 0) { - row->columns = ALLOC_N(ColumnHandle, row->size); - - if(row->columns != NULL) { - int i; - - memset(row->columns, 0, sizeof(ColumnHandle) * row->size); - for(i = 0; i < row->size; i++) { - VALUE index, - name, - alias, - scale, - items; - - index = INT2NUM(i); - name = rb_funcall(results, rb_intern("column_name"), 1, index); - alias = rb_funcall(results, rb_intern("column_alias"), 1, index); - strcpy(row->columns[i].name, StringValuePtr(name)); - strcpy(row->columns[i].alias, StringValuePtr(alias)); - items = rb_ary_entry(data, i); - row->columns[i].value = rb_ary_entry(items, 0); - row->columns[i].type = rb_ary_entry(items, 1); - row->columns[i].scale = rb_funcall(results, rb_intern("column_scale"), 1, index); - - if(TYPE(rb_ary_entry(items, 1)) == T_NIL) { - fprintf(stderr, "Nil column type encountered.\n"); - } - } - } else { - rb_raise(rb_eNoMemError, "Memory allocation failure populating row."); - } - } - +static VALUE initializeRow(VALUE self, VALUE results, VALUE number) { + rb_ivar_set(self, AT_NUMBER_ID, number); + rb_ivar_set(self, AT_COLUMNS_ID, toRowColumns(results)); return(self); } /** @@ -191,15 +153,11 @@ * * @return The number of columns that make up the row. * */ static VALUE columnsInRow(VALUE self) { - RowHandle *row = NULL; - - Data_Get_Struct(self, RowHandle, row); - - return(INT2NUM(row->size)); + return LONG2NUM(RARRAY_LEN(getColumns(self))); } /** * This function provides the number method for the Row class. @@ -208,11 +166,11 @@ * * @param A reference to the row number. * */ static VALUE getRowNumber(VALUE self) { - return(rb_iv_get(self, "@number")); + return(rb_ivar_get(self, AT_NUMBER_ID)); } /** * This function provides the column_name method for the Row class. @@ -223,21 +181,11 @@ * @return A reference to the column name or nil if an invalid index is * specified. * */ static VALUE getColumnName(VALUE self, VALUE index) { - VALUE name = Qnil; - RowHandle *row = NULL; - int number = 0; - - Data_Get_Struct(self, RowHandle, row); - number = TYPE(index) == T_FIXNUM ? FIX2INT(index) : NUM2INT(index); - if(number >= 0 && number < row->size) { - name = rb_str_new2(row->columns[number].name); - } - - return(name); + return rb_ivar_get(getMetadata(getColumns(self), index), AT_NAME_ID); } /** * This function provides the column_alias method for the Row class. @@ -248,21 +196,11 @@ * @return A reference to the column alias or nil if an invalid index is * specified. * */ static VALUE getColumnAlias(VALUE self, VALUE index) { - VALUE alias = Qnil; - RowHandle *row = NULL; - int number = 0; - - Data_Get_Struct(self, RowHandle, row); - number = TYPE(index) == T_FIXNUM ? FIX2INT(index) : NUM2INT(index); - if(number >= 0 && number < row->size) { - alias = rb_str_new2(row->columns[number].alias); - } - - return(alias); + return rb_ivar_get(getMetadata(getColumns(self), index), AT_ALIAS_ID); } /** * This function provides the [] method for the Row class. @@ -274,45 +212,21 @@ * @return A reference to the column value or nil if an invalid column index * is specified. * */ static VALUE getColumnValue(VALUE self, VALUE index) { - VALUE value = Qnil; - RowHandle *row = NULL; + VALUE fld; - Data_Get_Struct(self, RowHandle, row); if(TYPE(index) == T_STRING) { - char name[32]; - int i, - done = 0; - VALUE flag = getFireRubySetting("ALIAS_KEYS"); - - strcpy(name, StringValuePtr(index)); - for(i = 0; i < row->size && done == 0; i++) { - int match; - - /* Check whether its column name or column alias to compare on. */ - if(flag == Qtrue) { - match = strcmp(name, row->columns[i].alias); - } else { - match = strcmp(name, row->columns[i].name); - } - - if(match == 0) { - value = row->columns[i].value; - done = 1; - } - } + fld = metadataScan(self, index, AT_KEY_ID); } else { - int number = TYPE(index) == T_FIXNUM ? FIX2INT(index) : NUM2INT(index); - - if(number >= 0 && number < row->size) { - value = row->columns[number].value; - } + fld = getColumn(getColumns(self), index); } - - return(value); + if(Qnil == fld) { + return(Qnil); + } + return rb_ivar_get(fld, AT_VALUE_ID); } /** * This function provides the each method for the Row class. @@ -326,26 +240,14 @@ */ VALUE eachColumn(VALUE self) { VALUE result = Qnil; if(rb_block_given_p()) { - RowHandle *row = NULL; - int i; - VALUE flag = getFireRubySetting("ALIAS_KEYS"); - - Data_Get_Struct(self, RowHandle, row); - for(i = 0; i < row->size; i++) { - VALUE parameters = rb_ary_new(); - - /* Decide whether we're keying on column name or alias. */ - if(flag == Qtrue) { - rb_ary_push(parameters, rb_str_new2(row->columns[i].alias)); - } else { - rb_ary_push(parameters, rb_str_new2(row->columns[i].name)); - } - rb_ary_push(parameters, row->columns[i].value); - result = rb_yield(parameters); + long i; + VALUE columns = getColumns(self); + for(i = 0; i < RARRAY_LEN(columns); i++) { + result = rb_yield(keyValuePair(rb_ary_entry(columns, i))); } } return(result); } @@ -364,21 +266,14 @@ */ VALUE eachColumnKey(VALUE self) { VALUE result = Qnil; if(rb_block_given_p()) { - RowHandle *row = NULL; - int i; - VALUE flag = getFireRubySetting("ALIAS_KEYS"); - - Data_Get_Struct(self, RowHandle, row); - for(i = 0; i < row->size; i++) { - if(flag == Qtrue) { - result = rb_yield(rb_str_new2(row->columns[i].alias)); - } else { - result = rb_yield(rb_str_new2(row->columns[i].name)); - } + long i; + VALUE columns = getColumns(self); + for(i = 0; i < RARRAY_LEN(columns); i++) { + result = rb_yield(columnKey(rb_ary_entry(columns, i))); } } return(result); } @@ -396,16 +291,14 @@ */ VALUE eachColumnValue(VALUE self) { VALUE result = Qnil; if(rb_block_given_p()) { - RowHandle *row = NULL; - int i; - - Data_Get_Struct(self, RowHandle, row); - for(i = 0; i < row->size; i++) { - result = rb_yield(row->columns[i].value); + long i; + VALUE columns = getColumns(self); + for(i = 0; i < RARRAY_LEN(columns); i++) { + result = rb_yield(rb_ivar_get(rb_ary_entry(columns, i), AT_VALUE_ID)); } } return(result); } @@ -458,34 +351,14 @@ * @return True if the row possesses the specified column name, false * otherwise. * */ VALUE hasColumnKey(VALUE self, VALUE name) { - VALUE result = Qfalse; - RowHandle *row = NULL; - char text[32]; - int i; - VALUE flag = getFireRubySetting("ALIAS_KEYS"); - - Data_Get_Struct(self, RowHandle, row); - strcpy(text, StringValuePtr(name)); - for(i = 0; i < row->size && result == Qfalse; i++) { - int match; - - /* Check whether key is column name or alias. */ - if(flag == Qtrue) { - match = strcmp(text, row->columns[i].alias); - } else { - match = strcmp(text, row->columns[i].name); - } - - if(match == 0) { - result = Qtrue; - } + if(Qnil == metadataScan(self, name, AT_KEY_ID)) { + return Qfalse; } - - return(result); + return Qtrue; } /** * This function provides the has_column? method for the Row class. @@ -497,24 +370,14 @@ * @return True if the row possesses the specified column name, false * otherwise. * */ VALUE hasColumnName(VALUE self, VALUE name) { - VALUE result = Qfalse; - RowHandle *row = NULL; - char text[32]; - int i; - - Data_Get_Struct(self, RowHandle, row); - strcpy(text, StringValuePtr(name)); - for(i = 0; i < row->size && result == Qfalse; i++) { - if(strcmp(text, row->columns[i].name) == 0) { - result = Qtrue; - } + if(Qnil == metadataScan(self, name, AT_NAME_ID)) { + return Qfalse; } - - return(result); + return Qtrue; } /** * This function provides the has_alias? method for the Row class. @@ -526,24 +389,14 @@ * @return True if the row possesses the specified column alias, false * otherwise. * */ VALUE hasColumnAlias(VALUE self, VALUE name) { - VALUE result = Qfalse; - RowHandle *row = NULL; - char text[32]; - int i; - - Data_Get_Struct(self, RowHandle, row); - strcpy(text, StringValuePtr(name)); - for(i = 0; i < row->size && result == Qfalse; i++) { - if(strcmp(text, row->columns[i].alias) == 0) { - result = Qtrue; - } + if(Qnil == metadataScan(self, name, AT_ALIAS_ID)) { + return Qfalse; } - - return(result); + return Qtrue; } /** * This function provides the has_value? method for the Row class. @@ -553,23 +406,20 @@ * * @return True if the row contains a matching value, false otherwise. * */ VALUE hasColumnValue(VALUE self, VALUE value) { - VALUE result = Qfalse; - RowHandle *row = NULL; - int i; - - Data_Get_Struct(self, RowHandle, row); - for(i = 0; i < row->size && result == Qfalse; i++) { - result = rb_funcall(row->columns[i].value, rb_intern("eql?"), 1, value); + long i; + VALUE columns = getColumns(self); + for(i = 0; i < RARRAY_LEN(columns); i++) { + if(Qtrue == rb_funcall(rb_ivar_get(rb_ary_entry(columns, i), AT_VALUE_ID), EQL_ID, 1, value)) { + return(Qtrue); + } } - - return(result); + return(Qfalse); } - /** * This function fetches a list of column index keys for a Row object. What the * keys are depends on the library settings, but they will either be the column * names or the column aliases. * @@ -577,20 +427,11 @@ * * @return A reference to an array containing the row keys. * */ VALUE getColumnKeys(VALUE self) { - VALUE flag = getFireRubySetting("ALIAS_KEYS"), - keys = Qnil; - - if(flag == Qtrue) { - keys = getColumnAliases(self); - } else { - keys = getColumnNames(self); - } - - return(keys); + return rowCollect(self, AT_KEY_ID, 1); } /** * This function provides the keys method for the Row class. @@ -599,20 +440,11 @@ * * @return A reference to an array containing the row column names. * */ VALUE getColumnNames(VALUE self) { - VALUE result = rb_ary_new(); - RowHandle *row = NULL; - int i; - - Data_Get_Struct(self, RowHandle, row); - for(i = 0; i < row->size; i++) { - rb_ary_push(result, rb_str_new2(row->columns[i].name)); - } - - return(result); + return rowCollect(self, AT_NAME_ID, 1); } /** * This function provides the aliases method for the Row class. @@ -621,20 +453,11 @@ * * @return A reference to an array containing the row column aliases. * */ VALUE getColumnAliases(VALUE self) { - VALUE result = rb_ary_new(); - RowHandle *row = NULL; - int i; - - Data_Get_Struct(self, RowHandle, row); - for(i = 0; i < row->size; i++) { - rb_ary_push(result, rb_str_new2(row->columns[i].alias)); - } - - return(result); + return rowCollect(self, AT_ALIAS_ID, 1); } /** * This function provides the values method for the Row class. @@ -643,20 +466,11 @@ * * @return A reference to an array containing the row column names. * */ VALUE getColumnValues(VALUE self) { - VALUE result = rb_ary_new(); - RowHandle *row = NULL; - int i; - - Data_Get_Struct(self, RowHandle, row); - for(i = 0; i < row->size; i++) { - rb_ary_push(result, row->columns[i].value); - } - - return(result); + return rowCollect(self, AT_VALUE_ID, 0); } /** * This function provides the get_base_type method for the Row class. @@ -666,31 +480,12 @@ * * @return An Symbol containing the base type details. * */ VALUE getColumnBaseType(VALUE self, VALUE index) { - VALUE result = Qnil; - - if(TYPE(index) == T_FIXNUM) { - RowHandle *row = NULL; - - Data_Get_Struct(self, RowHandle, row); - if(row != NULL) { - int offset = FIX2INT(index); - - /* Correct negative index values. */ - if(offset < 0) { - offset = row->size + offset; - } - - if(offset >= 0 && offset < row->size) { - result = row->columns[offset].type; - } - } - } - - return(result); + VALUE columns = getColumns(self); + return rb_ivar_get(getMetadata(columns, checkRowOffset(columns, index)), AT_TYPE_ID); } /** * This function provides the column_scale method for the Row class. * @@ -701,31 +496,12 @@ * @return An Integer representing the scale of the column, or nil if an * invalid column was specified. * */ static VALUE getColumnScale(VALUE self, VALUE index) { - VALUE result = Qnil; - - if(TYPE(index) == T_FIXNUM) { - RowHandle *row = NULL; - - Data_Get_Struct(self, RowHandle, row); - if(row != NULL) { - int offset = FIX2INT(index); - - /* Correct negative index values. */ - if(offset < 0) { - offset = row->size + offset; - } - - if(offset >= 0 && offset < row->size) { - result = row->columns[offset].scale; - } - } - } - - return(result); + VALUE columns = getColumns(self); + return rb_ivar_get(getMetadata(columns, checkRowOffset(columns, index)), AT_SCALE_ID); } /** * This function provides the select method for the Row class. @@ -735,37 +511,25 @@ * @return An array containing the entries selected by the block passed to the * function. * */ VALUE selectRowEntries(VALUE self) { - VALUE result = Qnil, - flag = getFireRubySetting("ALIAS_KEYS"); - RowHandle *row = NULL; - int i; + long i; + VALUE result = rb_ary_new(), + columns = getColumns(self); if(!rb_block_given_p()) { rb_raise(rb_eStandardError, "No block specified in call to Row#select."); } - result = rb_ary_new(); - Data_Get_Struct(self, RowHandle, row); - for(i = 0; i < row->size; i++) { - VALUE parameters = rb_ary_new(); - - /* Check whether we're keying on column name or alias. */ - if(flag == Qtrue) { - rb_ary_push(parameters, rb_str_new2(row->columns[i].alias)); - } else { - rb_ary_push(parameters, rb_str_new2(row->columns[i].name)); - } - rb_ary_push(parameters, row->columns[i].value); + for(i = 0; i < RARRAY_LEN(columns); i++) { + VALUE parameters = keyValuePair(rb_ary_entry(columns, i)); if(rb_yield(parameters) == Qtrue) { rb_ary_push(result, parameters); } } - return(result); } /** @@ -775,27 +539,16 @@ * * @return An array containing the entries from the Row object. * */ VALUE rowToArray(VALUE self) { + long i; VALUE result = rb_ary_new(), - flag = getFireRubySetting("ALIAS_KEYS"); - RowHandle *row = NULL; - int i; + columns = getColumns(self); - Data_Get_Struct(self, RowHandle, row); - for(i = 0; i < row->size; i++) { - VALUE parameters = rb_ary_new(); - - /* Check whether we're keying on column name or alias. */ - if(flag == Qtrue) { - rb_ary_push(parameters, rb_str_new2(row->columns[i].alias)); - } else { - rb_ary_push(parameters, rb_str_new2(row->columns[i].name)); - } - rb_ary_push(parameters, row->columns[i].value); - rb_ary_push(result, parameters); + for(i = 0; i < RARRAY_LEN(columns); i++) { + rb_ary_push(result, keyValuePair(rb_ary_entry(columns, i))); } return(result); } @@ -807,26 +560,17 @@ * * @return A hash containing the entries from the Row object. * */ VALUE rowToHash(VALUE self) { + long i; VALUE result = rb_hash_new(), - flag = getFireRubySetting("ALIAS_KEYS"); - RowHandle *row = NULL; - int i; + columns = getColumns(self); - Data_Get_Struct(self, RowHandle, row); - for(i = 0; i < row->size; i++) { - VALUE key = Qnil; - - /* Check if we're keying on column name or alias. */ - if(flag == Qtrue) { - key = rb_str_new2(row->columns[i].alias); - } else { - key = rb_str_new2(row->columns[i].name); - } - rb_hash_aset(result, key, row->columns[i].value); + for(i = 0; i < RARRAY_LEN(columns); i++) { + VALUE fld = rb_ary_entry(columns, i); + rb_hash_aset(result, columnKey(fld), rb_ivar_get(fld, AT_VALUE_ID)); } return(result); } @@ -840,12 +584,12 @@ * * @return An array of the values that match the column names specified. * */ VALUE rowValuesAt(int size, VALUE *keys, VALUE self) { - VALUE result = rb_ary_new(); int i; + VALUE result = rb_ary_new(); for(i = 0; i < size; i++) { rb_ary_push(result, getColumnValue(self, keys[i])); } @@ -861,15 +605,17 @@ * @param number A reference to the number to be associated with the row. * * @return A reference to the Row object created. * */ -VALUE rb_row_new(VALUE results, VALUE data, VALUE number) { - VALUE row = allocateRow(cRow); +VALUE rb_row_new(VALUE results, VALUE number) { + VALUE row = rb_funcall(cRow, NEW_ID, 2, results, number); - initializeRow(row, results, data, number); + RB_GC_GUARD(row); + initializeRow(row, results, number); + return(row); } /** @@ -879,13 +625,26 @@ * @param module A reference to the module that the Row class will be created * under. * */ void Init_Row(VALUE module) { + Init_TypeMap(); + EQL_ID = rb_intern("eql?"); + FETCH_ID = rb_intern("fetch"); + AT_NAME_ID = rb_intern("@name"); + AT_ALIAS_ID = rb_intern("@alias"); + AT_KEY_ID = rb_intern("@key"); + AT_SCALE_ID = rb_intern("@scale"); + AT_COLUMNS_ID = rb_intern("@columns"); + AT_NUMBER_ID = rb_intern("@number"); + AT_VALUE_ID = rb_intern("@value"); + AT_TYPE_ID = rb_intern("@type"); + NEW_ID = rb_intern("new"); + AT_COLUMN_ID = rb_intern("@column"); + cRow = rb_define_class_under(module, "Row", rb_cObject); - rb_define_alloc_func(cRow, allocateRow); rb_include_module(cRow, rb_mEnumerable); - rb_define_method(cRow, "initialize", initializeRow, 3); + rb_define_method(cRow, "initialize", initializeRow, 2); rb_define_method(cRow, "number", getRowNumber, 0); rb_define_method(cRow, "column_count", columnsInRow, 0); rb_define_method(cRow, "column_name", getColumnName, 1); rb_define_method(cRow, "column_alias", getColumnAlias, 1); rb_define_method(cRow, "column_scale", getColumnScale, 1);