Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Using object literals to append rows to tables #42

Merged
merged 15 commits into from
Mar 7, 2014
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/tightdb/objc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ nobase_subinclude_HEADERS_EXTRA_UNINSTALL = *.h
lib_LIBRARIES = libtightdb-objc.a

libtightdb_objc_a_SOURCES = table_objc.mm cursor_objc.mm group_objc.mm query_objc.mm \
group_shared_objc.mm
group_shared_objc.mm support.mm

libtightdb_objc_a_CFLAGS = -fobjc-arc -fobjc-abi-version=2
libtightdb_objc_a_LDFLAGS = -fobjc-link-runtime -framework Cocoa
Expand Down
29 changes: 29 additions & 0 deletions src/tightdb/objc/support.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*************************************************************************
*
* TIGHTDB CONFIDENTIAL
* __________________
*
* [2011] - [2014] TightDB Inc
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of TightDB Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to TightDB Incorporated
* and its suppliers and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from TightDB Incorporated.
*
**************************************************************************/

#ifndef TIGHTDB_OBJC_SUPPORT_H
#define TIGHTDB_OBJC_SUPPORT_H

#import <Foundation/Foundation.h>
#include <tightdb/descriptor.hpp>

BOOL verify_row(const tightdb::Descriptor& descr, NSArray * data);
BOOL insert_row(size_t ndx, tightdb::Table& table, NSArray * data);
#endif /* TIGHTDB_OBJC_SUPPORT_H */
192 changes: 192 additions & 0 deletions src/tightdb/objc/support.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#import <Foundation/Foundation.h>

#include <tightdb/descriptor.hpp>

#import "table.h"

using namespace tightdb;

BOOL verify_row(const Descriptor& descr, NSArray * data)
{
NSEnumerator *enumerator = [data objectEnumerator];
id obj;

/* type encodings: http://nshipster.com/type-encodings/ */
size_t col_ndx = 0;
while (obj = [enumerator nextObject]) {
DataType type = descr.get_column_type(col_ndx);
switch (type) {
case type_String:
if (![obj isKindOfClass:[NSString class]])
return NO;
break;
case type_Bool:
if ([obj isKindOfClass:[NSNumber class]]) {
const char * data_type = [obj objCType];
const char dt = data_type[0];
if (dt != 'B')
return NO;
}
break;
case type_DateTime:
if ([obj isKindOfClass:[NSNumber class]]) {
const char * data_type = [obj objCType];
const char dt = data_type[0];
/* time_t is an integer */
if (dt == 'i' || dt == 's' || dt == 'l' || dt == 'q' ||
dt == 'I' || dt == 'S' || dt == 'L' || dt == 'Q')
break;
else {
return NO;
}
}
else {
return NO;
}
break;
case type_Int:
if ([obj isKindOfClass:[NSNumber class]]) {
const char * data_type = [obj objCType];
const char dt = data_type[0];
/* FIXME: what about: 'c', 'C' */
if (dt == 'i' || dt == 's' || dt == 'l' || dt == 'q' ||
dt == 'I' || dt == 'S' || dt == 'L' || dt == 'Q')
break;
else
return NO;
}
else {
return NO;
}
break;
case type_Float:
if ([obj isKindOfClass:[NSNumber class]]) {
const char * data_type = [obj objCType];
const char dt = data_type[0];
/* FIXME: what about: 'c', 'C' */
if (dt == 'i' || dt == 's' || dt == 'l' || dt == 'q' ||
dt == 'I' || dt == 'S' || dt == 'L' || dt == 'Q' ||
dt == 'f')
break;
else
return NO;
}
else
return NO;
break; /* FIXME: remove */
case type_Double:
if ([obj isKindOfClass:[NSNumber class]]) {
const char * data_type = [obj objCType];
const char dt = data_type[0];
/* FIXME: what about: 'c', 'C' */
if (dt == 'i' || dt == 's' || dt == 'l' || dt == 'q' ||
dt == 'I' || dt == 'S' || dt == 'L' || dt == 'Q' ||
dt == 'f' || dt == 'd')
break;
else
return NO;
}
else
return NO;
break; /* FIXME: remove */
case type_Binary:
if (![obj isKindOfClass:[TightdbBinary class]])
return NO;
break;
case type_Mixed:
break; /* everything goes */
case type_Table:
if ([obj isKindOfClass:[NSArray class]]) {
if ([obj count] == 0)
break; /* empty subtable */
id subobj;
ConstDescriptorRef subdescr = descr.get_subdescriptor(col_ndx);
NSEnumerator *subenumerator = [obj objectEnumerator];
while (subobj = [subenumerator nextObject]) {
if (![subobj isKindOfClass:[NSArray class]])
return NO;
if (!verify_row(*subdescr, (NSArray *)subobj))
return NO;
}
}
else {
return NO;
}
break;

}
++col_ndx;
}
return YES;
}

BOOL insert_row(size_t row_ndx, tightdb::Table& table, NSArray * data)
{
/*
Assumption:
- data has been validated by verify_row
*/

NSEnumerator *enumerator = [data objectEnumerator];
id obj;

/* FIXME: handling of tightdb exceptions => return NO */
size_t col_ndx = 0;
while (obj = [enumerator nextObject]) {
DataType type = table.get_column_type(col_ndx);
switch (type) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To quote a conversation we had earlier with @kspangsege and @kneth :

The general paradigm I use is

switch(column_type)
{
case type_Int:
    ...
    goto ok;
case type_Float:
    ...
    goto ok;
case type_Double:
    ...
    goto ok;
case type_Bool:
case type_DateTime:
case type_String:
case type_Binary:
case type_Mixed:
case type_Table:
    RAISE_COLUMN_TYPE(column_ndx,
        "either int, float, or double");
    return Qnil;
}

RAISE_UNKNOWN_TIGHTDB_TYPE(); // returns from the function

ok:
    return result;

The effect is that the compiler will warn of any missing cases, and if
any new cases are added (to core) after the extension has been
compiled, the extension will duly fail.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is nothing common to do after "ok:", I would definitely 'return result' in the 'case' instead of 'goto ok'.

case type_Bool:
table.insert_bool(col_ndx, row_ndx, bool([obj boolValue]));
break;
case type_DateTime:
table.insert_datetime(col_ndx, row_ndx, time_t([obj longValue]));
break;
case type_Int:
table.insert_int(col_ndx, row_ndx, int64_t([obj longValue]));
break;
case type_Float:
table.insert_float(col_ndx, row_ndx, float([obj floatValue]));
break;
case type_Double:
table.insert_double(col_ndx, row_ndx, double([obj doubleValue]));
break;
case type_String:
{
StringData sd([obj UTF8String]);
table.insert_string(col_ndx, row_ndx, sd);
}
break;
case type_Binary:
{
BinaryData bd([obj getData], [obj getSize]);
table.insert_binary(col_ndx, row_ndx, bd);
}
break;
case type_Table:
if ([obj count]) {
table.clear_subtable(col_ndx, row_ndx);
}
else {
// Clear sub-table to prepare for new values
table.clear_subtable(col_ndx, row_ndx);
table.insert_subtable(col_ndx, row_ndx);
TableRef subtable = table.get_subtable(col_ndx, row_ndx);
NSEnumerator *subenumerator = [obj objectEnumerator];
id subobj;
size_t subrow_ndx = 0;
while (subobj = [subenumerator nextObject]) {
if (!insert_row(subrow_ndx, *subtable, subobj))
return NO;
++subrow_ndx;
}
}
break;
}
++col_ndx;
}
table.insert_done();

/* FIXME: subtables */

return YES;
}
2 changes: 2 additions & 0 deletions src/tightdb/objc/table.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@

-(TightdbCursor *)insertRowAtIndex:(size_t)ndx;

-(BOOL)appendRow:(NSArray *)data;

-(BOOL)insertRow:(size_t)ndx;
-(BOOL)insertRow:(size_t)ndx error:(NSError *__autoreleasing *)error;

Expand Down
15 changes: 15 additions & 0 deletions src/tightdb/objc/table_objc.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// TightDB
//

#import <Foundation/Foundation.h>

#include <tightdb/util/unique_ptr.hpp>
#include <tightdb/table.hpp>
#include <tightdb/descriptor.hpp>
Expand All @@ -14,6 +16,7 @@
#import <tightdb/objc/query.h>
#import <tightdb/objc/query_priv.h>
#import <tightdb/objc/cursor.h>
#import <tightdb/objc/support.h>

#include <tightdb/objc/util.hpp>

Expand Down Expand Up @@ -673,6 +676,18 @@ -(TightdbCursor*)insertRowAtIndex:(size_t)ndx
return [[TightdbCursor alloc] initWithTable:self ndx:ndx];
}

-(BOOL)appendRow:(NSArray*)data
{
tightdb::Table& table = *m_table;
tightdb::ConstDescriptorRef desc = table.get_descriptor();
if (!verify_row(*desc, data)) {
return NO;
}

/* append row */
return insert_row(table.size(), table, data);
}

-(BOOL)insertRow:(size_t)ndx
{
return [self insertRow:ndx error:nil];
Expand Down
79 changes: 77 additions & 2 deletions src/tightdb/objc/test/dynamic_table.m
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,89 @@ - (void)testTable
[cursor setInt:0 inColumn:0];
[cursor setInt:10 inColumn:1];


// Verify
if ([_table getIntInColumn:0 atRow:ndx] != 0)
STFail(@"First not zero");
if ([_table getIntInColumn:1 atRow:ndx] != 10)
STFail(@"Second not 10");

// Add row using object literate
TightdbTable* _table2 = [[TightdbTable alloc] init];
[_table2 addColumnWithType:tightdb_Int andName:@"first"];
if (![_table2 appendRow:@[ @1 ]])
STFail(@"Impossible!");
if ([_table2 count] != 1)
STFail(@"Excepted 1 row");
if (![_table2 appendRow:@[ @2 ]])
STFail(@"Impossible!");
if ([_table2 count] != 2)
STFail(@"Excepted 2 rows");
if ([_table2 getIntInColumn:0 atRow:0] != 1)
STFail(@"Value 1 excepted");
if ([_table2 getIntInColumn:0 atRow:1] != 2)
STFail(@"Value 2 excepted");
if ([_table2 appendRow:@[@"Hello"]])
STFail(@"Wrong type");
if ([_table2 appendRow:@[@1, @"Hello"]])
STFail(@"Wrong number of columns");

TightdbTable* _table3 = [[TightdbTable alloc] init];
[_table3 addColumnWithType:tightdb_Int andName:@"first"];
[_table3 addColumnWithType:tightdb_String andName:@"second"];
if (![_table3 appendRow:@[@1, @"Hello"]])
STFail(@"appendRow 1");
if ([_table3 count] != 1)
STFail(@"1 row expected");
if ([_table3 getIntInColumn:0 atRow:0] != 1)
STFail(@"Value 1 excepted");
if (![[_table3 getStringInColumn:1 atRow:0] isEqualToString:@"Hello"])
STFail(@"Value 'Hello' excepted");
if ([_table3 appendRow:@[@1, @2]])
STFail(@"appendRow 2");

TightdbTable* _table4 = [[TightdbTable alloc] init];
[_table4 addColumnWithType:tightdb_Double andName:@"first"];
if (![_table4 appendRow:@[@3.14]]) /* double is default */
STFail(@"Cannot insert 'double'");
if ([_table4 count] != 1)
STFail(@"1 row excepted");

TightdbTable* _table5 = [[TightdbTable alloc] init];
[_table5 addColumnWithType:tightdb_Float andName:@"first"];
if (![_table5 appendRow:@[@3.14F]]) /* F == float */
STFail(@"Cannot insert 'float'");
if ([_table5 count] != 1)
STFail(@"1 row excepted");

TightdbTable* _table6 = [[TightdbTable alloc] init];
[_table6 addColumnWithType:tightdb_Date andName:@"first"];
if (![_table6 appendRow:@[@1000000000]]) /* 2001-09-09 01:46:40 */
STFail(@"Cannot insert 'time_t'");
if ([_table6 count] != 1)
STFail(@"1 row excepted");


const char bin[4] = { 0, 1, 2, 3 };
TightdbBinary* bin2 = [[TightdbBinary alloc] initWithData:bin size:sizeof bin];
TightdbTable* _table7 = [[TightdbTable alloc] init];
[_table7 addColumnWithType:tightdb_Binary andName:@"first"];
if (![_table7 appendRow:@[bin2]])
STFail(@"Cannot insert 'binary'");
if ([_table7 count] != 1)
STFail(@"1 row excepted");

TightdbTable* _table8 = [[TightdbTable alloc] init];
[_table8 addColumnWithType:tightdb_Int andName:@"first"];
TightdbDescriptor* _descr8 = [_table8 getDescriptor];
TightdbDescriptor* _subdescr8 = [_descr8 addColumnTable:@"second"];
[_subdescr8 addColumnWithType:tightdb_Int andName:@"TableCol_IntCol"];
if (![_table8 appendRow:@[@1, @[]]])
STFail(@"Cannot insert empty subtable");
if ([_table8 count] != 1)
STFail(@"1 row excepted");
if (![_table8 appendRow:@[@2, @[@[@3]]]])
STFail(@"Cannot insert subtable");
if ([_table8 count] != 2)
STFail(@"2 rows excepted");
}

- (void)testDataTypes_Dynamic
Expand Down