SlideShare una empresa de Scribd logo
1 de 143
SQLite Techniques

           Ben Scheirman
            @subdigital

           Code / Slides:
github.com/subdigital/iphonedevcon-
              sandiego
SQLite is....

Single-user

File-based

Simple

Cross Platform

A subset of full ANSI SQL
SQLite API


Pure C API

Lots of strange-looking, hard to
debug code

Best choice is to abstract it away
SQLite Tools



SQLite Manager Add-in for Firefox

sqlite3 command line utility
Writable Databases


Your app bundle is signed!

(That means the contents can't
change)
Your App Sandbox




Your App Bundle


       Resources
                   Documents
Your App Sandbox




Your App Bundle


       Resources
                   Documents
Creating the
  database
Getting Resource
      Paths
Getting Resource
                Paths

//fetches
path
for
foo.db
Getting Resource
                Paths

//fetches
path
for
foo.db
NSString
*resourcePath
=
[[NSBundle
mainBundle]
Getting Resource
                Paths

//fetches
path
for
foo.db
NSString
*resourcePath
=
[[NSBundle
mainBundle]





























pathForResource:@"foo"
Getting Resource
                Paths

//fetches
path
for
foo.db
NSString
*resourcePath
=
[[NSBundle
mainBundle]





























pathForResource:@"foo"





























ofType:@"db"];
Finding the Documents
      Directory
Finding the Documents
      Directory

NSArray
*paths
=
NSSearchPathForDirectoriesInDomains(

Finding the Documents
      Directory

NSArray
*paths
=
NSSearchPathForDirectoriesInDomains(




























NSDocumentDirectory,

Finding the Documents
      Directory

NSArray
*paths
=
NSSearchPathForDirectoriesInDomains(




























NSDocumentDirectory,




























NSUserDomainMask,

Finding the Documents
      Directory

NSArray
*paths
=
NSSearchPathForDirectoriesInDomains(




























NSDocumentDirectory,




























NSUserDomainMask,




























YES);
Finding the Documents
      Directory

NSArray
*paths
=
NSSearchPathForDirectoriesInDomains(




























NSDocumentDirectory,




























NSUserDomainMask,




























YES);

NSString
*documentsDirectory
=
[paths
objectAtIndex:0];
Copy a default
database on startup
Copy a default
     database on startup
  //in applicationDidFinishLaunching

  NSString *sourcePath = [[NSBundle mainBundle]
   pathForResource:@"app" ofType:@"db"];

NSString *targetPath = ...
 NSFileManager *fm = [NSFileManager defaultManager];

if(![fm fileExistsAtPath:targetPath]) {
  [fm copyItemAtPath:sourcePath toPath:targetPath error:&error];
}
Copy a default
     database on startup
  //in applicationDidFinishLaunching

  NSString *sourcePath = [[NSBundle mainBundle]
   pathForResource:@"app" ofType:@"db"];

NSString *targetPath = ...
 NSFileManager *fm = [NSFileManager defaultManager];

if(![fm fileExistsAtPath:targetPath]) {
  [fm copyItemAtPath:sourcePath toPath:targetPath error:&error];
}
Copy a default
     database on startup
  //in applicationDidFinishLaunching

  NSString *sourcePath = [[NSBundle mainBundle]
   pathForResource:@"app" ofType:@"db"];

NSString *targetPath = ...
 NSFileManager *fm = [NSFileManager defaultManager];

if(![fm fileExistsAtPath:targetPath]) {
  [fm copyItemAtPath:sourcePath toPath:targetPath error:&error];
}
Copy a default
     database on startup
  //in applicationDidFinishLaunching

  NSString *sourcePath = [[NSBundle mainBundle]
   pathForResource:@"app" ofType:@"db"];

NSString *targetPath = ...
 NSFileManager *fm = [NSFileManager defaultManager];

if(![fm fileExistsAtPath:targetPath]) {
  [fm copyItemAtPath:sourcePath toPath:targetPath error:&error];
}
Copy a default
     database on startup
  //in applicationDidFinishLaunching

  NSString *sourcePath = [[NSBundle mainBundle]
   pathForResource:@"app" ofType:@"db"];

NSString *targetPath = ...
 NSFileManager *fm = [NSFileManager defaultManager];

if(![fm fileExistsAtPath:targetPath]) {
  [fm copyItemAtPath:sourcePath toPath:targetPath error:&error];
}
Copy a default
     database on startup
  //in applicationDidFinishLaunching

  NSString *sourcePath = [[NSBundle mainBundle]
   pathForResource:@"app" ofType:@"db"];

NSString *targetPath = ...
 NSFileManager *fm = [NSFileManager defaultManager];

if(![fm fileExistsAtPath:targetPath]) {
  [fm copyItemAtPath:sourcePath toPath:targetPath error:&error];
}
Copy a default
     database on startup
  //in applicationDidFinishLaunching

  NSString *sourcePath = [[NSBundle mainBundle]
   pathForResource:@"app" ofType:@"db"];

NSString *targetPath = ...
 NSFileManager *fm = [NSFileManager defaultManager];

if(![fm fileExistsAtPath:targetPath]) {
  [fm copyItemAtPath:sourcePath toPath:targetPath error:&error];
}
SQLite API Basics
Add the Framework
Add the Framework
Add the Framework
Add the Framework
SQLite API -
Opening a connection
SQLite API -
    Opening a connection
#import "sqlite3.h"
SQLite API -
    Opening a connection
#import "sqlite3.h"

sqlite3 *db;
SQLite API -
    Opening a connection
#import "sqlite3.h"

sqlite3 *db;

int result = sqlite3_open(PATH, &db);
SQLite API -
    Opening a connection
#import "sqlite3.h"

sqlite3 *db;

int result = sqlite3_open(PATH, &db);
if (result != SQLITE_OK) {
SQLite API -
    Opening a connection
#import "sqlite3.h"

sqlite3 *db;

int result = sqlite3_open(PATH, &db);
if (result != SQLITE_OK) {
   [NSException raise:@"SQLITE ERROR"
SQLite API -
    Opening a connection
#import "sqlite3.h"

sqlite3 *db;

int result = sqlite3_open(PATH, &db);
if (result != SQLITE_OK) {
   [NSException raise:@"SQLITE ERROR"
            format:@"Error %d", result];
SQLite API -
    Opening a connection
#import "sqlite3.h"

sqlite3 *db;

int result = sqlite3_open(PATH, &db);
if (result != SQLITE_OK) {
   [NSException raise:@"SQLITE ERROR"
            format:@"Error %d", result];
}
SQLite API -
    Opening a connection
#import "sqlite3.h"

sqlite3 *db;

int result = sqlite3_open(PATH, &db);
if (result != SQLITE_OK) {
   [NSException raise:@"SQLITE ERROR"
            format:@"Error %d", result];
}


//later
SQLite API -
    Opening a connection
#import "sqlite3.h"

sqlite3 *db;

int result = sqlite3_open(PATH, &db);
if (result != SQLITE_OK) {
   [NSException raise:@"SQLITE ERROR"
            format:@"Error %d", result];
}


//later
sqlite3_close(db);
SQLite API -
executing queries
SQLite API -
         executing queries
int sqlite3_exec(
SQLite API -
         executing queries
int sqlite3_exec(
     sqlite3 *db,
SQLite API -
         executing queries
int sqlite3_exec(
     sqlite3 *db,
     char *sql,
SQLite API -
         executing queries
int sqlite3_exec(
     sqlite3 *db,
     char *sql,
     int (*callback)(void *, int, char**, char**) callbackFunc,
SQLite API -
         executing queries
int sqlite3_exec(
     sqlite3 *db,
     char *sql,
     int (*callback)(void *, int, char**, char**) callbackFunc,
     void *context,
SQLite API -
         executing queries
int sqlite3_exec(
     sqlite3 *db,
     char *sql,
     int (*callback)(void *, int, char**, char**) callbackFunc,
     void *context,
     char **errorMessage
SQLite API -
         executing queries
int sqlite3_exec(
     sqlite3 *db,
     char *sql,
     int (*callback)(void *, int, char**, char**) callbackFunc,
     void *context,
     char **errorMessage
     );
SQLite API -
         executing queries
int sqlite3_exec(
     sqlite3 *db,
     char *sql,
     int (*callback)(void *, int, char**, char**) callbackFunc,
     void *context,
     char **errorMessage
     );


//callback signature
SQLite API -
         executing queries
int sqlite3_exec(
     sqlite3 *db,
     char *sql,
     int (*callback)(void *, int, char**, char**) callbackFunc,
     void *context,
     char **errorMessage
     );


//callback signature
int RowCallback(void * context,
SQLite API -
         executing queries
int sqlite3_exec(
     sqlite3 *db,
     char *sql,
     int (*callback)(void *, int, char**, char**) callbackFunc,
     void *context,
     char **errorMessage
     );


//callback signature
int RowCallback(void * context,
    int numColumns,
SQLite API -
         executing queries
int sqlite3_exec(
     sqlite3 *db,
     char *sql,
     int (*callback)(void *, int, char**, char**) callbackFunc,
     void *context,
     char **errorMessage
     );


//callback signature
int RowCallback(void * context,
    int numColumns,
    char** colValues,
SQLite API -
         executing queries
int sqlite3_exec(
     sqlite3 *db,
     char *sql,
     int (*callback)(void *, int, char**, char**) callbackFunc,
     void *context,
     char **errorMessage
     );


//callback signature
int RowCallback(void * context,
    int numColumns,
    char** colValues,
    char ** colNames);
SQLite API -
 Prepared Statements


More Powerful

Unfortunately much more code!
SQLite API-Prepared Statements
SQLite API-Prepared Statements


sqlite3 *db;
SQLite API-Prepared Statements


sqlite3 *db;
int result = sqlite3_open(DB_PATH, &db);
SQLite API-Prepared Statements


sqlite3 *db;
int result = sqlite3_open(DB_PATH, &db);
if(result != SQLITE_OK) {
SQLite API-Prepared Statements


sqlite3 *db;
int result = sqlite3_open(DB_PATH, &db);
if(result != SQLITE_OK) {
   //handle error
SQLite API-Prepared Statements


sqlite3 *db;
int result = sqlite3_open(DB_PATH, &db);
if(result != SQLITE_OK) {
   //handle error
}
SQLite API-Prepared Statements


sqlite3 *db;
int result = sqlite3_open(DB_PATH, &db);
if(result != SQLITE_OK) {
   //handle error
}
SQLite API-Prepared Statements


sqlite3 *db;
int result = sqlite3_open(DB_PATH, &db);
if(result != SQLITE_OK) {
   //handle error
}

sqlite3_stmt *stmt;
SQLite API-Prepared Statements


sqlite3 *db;
int result = sqlite3_open(DB_PATH, &db);
if(result != SQLITE_OK) {
   //handle error
}

sqlite3_stmt *stmt;
result = sqlite3_prepare_v2(db, sql_cstring, -1, &stmt, NULL);
SQLite API-Prepared Statements


sqlite3 *db;
int result = sqlite3_open(DB_PATH, &db);
if(result != SQLITE_OK) {
   //handle error
}

sqlite3_stmt *stmt;
result = sqlite3_prepare_v2(db, sql_cstring, -1, &stmt, NULL);
if(result != SQLITE_OK) {
SQLite API-Prepared Statements


sqlite3 *db;
int result = sqlite3_open(DB_PATH, &db);
if(result != SQLITE_OK) {
   //handle error
}

sqlite3_stmt *stmt;
result = sqlite3_prepare_v2(db, sql_cstring, -1, &stmt, NULL);
if(result != SQLITE_OK) {
   //handle error
SQLite API-Prepared Statements


sqlite3 *db;
int result = sqlite3_open(DB_PATH, &db);
if(result != SQLITE_OK) {
   //handle error
}

sqlite3_stmt *stmt;
result = sqlite3_prepare_v2(db, sql_cstring, -1, &stmt, NULL);
if(result != SQLITE_OK) {
   //handle error
}
SQLite API-Prepared Statements


sqlite3 *db;
int result = sqlite3_open(DB_PATH, &db);
if(result != SQLITE_OK) {
   //handle error
}

sqlite3_stmt *stmt;
result = sqlite3_prepare_v2(db, sql_cstring, -1, &stmt, NULL);
if(result != SQLITE_OK) {
   //handle error
}

//...
SQLite API-Prepared Statements


sqlite3 *db;
int result = sqlite3_open(DB_PATH, &db);
if(result != SQLITE_OK) {
   //handle error
}

sqlite3_stmt *stmt;
result = sqlite3_prepare_v2(db, sql_cstring, -1, &stmt, NULL);
if(result != SQLITE_OK) {
   //handle error
}

//...
SQLite API-Prepared Statements


sqlite3 *db;
int result = sqlite3_open(DB_PATH, &db);
if(result != SQLITE_OK) {
   //handle error
}

sqlite3_stmt *stmt;
result = sqlite3_prepare_v2(db, sql_cstring, -1, &stmt, NULL);
if(result != SQLITE_OK) {
   //handle error
}

//...
SQLite API-Prepared Statements


sqlite3 *db;
int result = sqlite3_open(DB_PATH, &db);
if(result != SQLITE_OK) {
   //handle error
}

sqlite3_stmt *stmt;
result = sqlite3_prepare_v2(db, sql_cstring, -1, &stmt, NULL);
if(result != SQLITE_OK) {
   //handle error
}

//...
SQLite API-Prepared Statements
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);

result = sqlite3_step(stmt);
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);

result = sqlite3_step(stmt);
if(result == SQLITE_DONE)
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);

result = sqlite3_step(stmt);
if(result == SQLITE_DONE)
   return [NSArray array]; //nothing to do!
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);

result = sqlite3_step(stmt);
if(result == SQLITE_DONE)
   return [NSArray array]; //nothing to do!
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);

result = sqlite3_step(stmt);
if(result == SQLITE_DONE)
   return [NSArray array]; //nothing to do!

NSMutableArray *results = [NSMutableArray array];
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);

result = sqlite3_step(stmt);
if(result == SQLITE_DONE)
   return [NSArray array]; //nothing to do!

NSMutableArray *results = [NSMutableArray array];
while(result == SQLITE_ROW) {
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);

result = sqlite3_step(stmt);
if(result == SQLITE_DONE)
   return [NSArray array]; //nothing to do!

NSMutableArray *results = [NSMutableArray array];
while(result == SQLITE_ROW) {
  NSMutableDictionary *row = [NSMutableDictionary dictionary];
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);

result = sqlite3_step(stmt);
if(result == SQLITE_DONE)
   return [NSArray array]; //nothing to do!

NSMutableArray *results = [NSMutableArray array];
while(result == SQLITE_ROW) {
  NSMutableDictionary *row = [NSMutableDictionary dictionary];
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);

result = sqlite3_step(stmt);
if(result == SQLITE_DONE)
   return [NSArray array]; //nothing to do!

NSMutableArray *results = [NSMutableArray array];
while(result == SQLITE_ROW) {
  NSMutableDictionary *row = [NSMutableDictionary dictionary];

  [self processRow:row forStatement:stmt];
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);

result = sqlite3_step(stmt);
if(result == SQLITE_DONE)
   return [NSArray array]; //nothing to do!

NSMutableArray *results = [NSMutableArray array];
while(result == SQLITE_ROW) {
  NSMutableDictionary *row = [NSMutableDictionary dictionary];

  [self processRow:row forStatement:stmt];

  [results addObject:row];
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);

result = sqlite3_step(stmt);
if(result == SQLITE_DONE)
   return [NSArray array]; //nothing to do!

NSMutableArray *results = [NSMutableArray array];
while(result == SQLITE_ROW) {
  NSMutableDictionary *row = [NSMutableDictionary dictionary];

  [self processRow:row forStatement:stmt];

  [results addObject:row];
  result = sqlite3_step(stmt);
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);

result = sqlite3_step(stmt);
if(result == SQLITE_DONE)
   return [NSArray array]; //nothing to do!

NSMutableArray *results = [NSMutableArray array];
while(result == SQLITE_ROW) {
  NSMutableDictionary *row = [NSMutableDictionary dictionary];

    [self processRow:row forStatement:stmt];

    [results addObject:row];
    result = sqlite3_step(stmt);
}
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);

result = sqlite3_step(stmt);
if(result == SQLITE_DONE)
   return [NSArray array]; //nothing to do!

NSMutableArray *results = [NSMutableArray array];
while(result == SQLITE_ROW) {
  NSMutableDictionary *row = [NSMutableDictionary dictionary];

    [self processRow:row forStatement:stmt];

    [results addObject:row];
    result = sqlite3_step(stmt);
}

sqlite_finalize(stmt);
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);

result = sqlite3_step(stmt);
if(result == SQLITE_DONE)
   return [NSArray array]; //nothing to do!

NSMutableArray *results = [NSMutableArray array];
while(result == SQLITE_ROW) {
  NSMutableDictionary *row = [NSMutableDictionary dictionary];

    [self processRow:row forStatement:stmt];

    [results addObject:row];
    result = sqlite3_step(stmt);
}

sqlite_finalize(stmt);
SQLite API-Prepared Statements
//INSERT INTO CARS(year, make) VALUES(?, ?)

//PARAMETERS HAVE A 1-BASED INDEX!
sqlite3_bind_int(stmt, 1 /* idx */, 5);
sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);

result = sqlite3_step(stmt);
if(result == SQLITE_DONE)
   return [NSArray array]; //nothing to do!

NSMutableArray *results = [NSMutableArray array];
while(result == SQLITE_ROW) {
  NSMutableDictionary *row = [NSMutableDictionary dictionary];

    [self processRow:row forStatement:stmt];

    [results addObject:row];
    result = sqlite3_step(stmt);
}

sqlite_finalize(stmt);

return results;
SQLite API-Prepared Statements
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
 for(int i=0; i<columnCount; i++) {
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
 for(int i=0; i<columnCount; i++) {
    //0-based index!
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
 for(int i=0; i<columnCount; i++) {
    //0-based index!
    const char * colName = sqlite3_column_name(stmt, i);
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
 for(int i=0; i<columnCount; i++) {
    //0-based index!
    const char * colName = sqlite3_column_name(stmt, i);
    int type = sqlite3_column_type(stmt, i);
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
 for(int i=0; i<columnCount; i++) {
    //0-based index!
    const char * colName = sqlite3_column_name(stmt, i);
    int type = sqlite3_column_type(stmt, i);

   id value = [NSNull null];
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
 for(int i=0; i<columnCount; i++) {
    //0-based index!
    const char * colName = sqlite3_column_name(stmt, i);
    int type = sqlite3_column_type(stmt, i);

   id value = [NSNull null];
   if(type == SQLITE_INTEGER) {
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
 for(int i=0; i<columnCount; i++) {
    //0-based index!
    const char * colName = sqlite3_column_name(stmt, i);
    int type = sqlite3_column_type(stmt, i);

   id value = [NSNull null];
   if(type == SQLITE_INTEGER) {
      value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)];
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
 for(int i=0; i<columnCount; i++) {
    //0-based index!
    const char * colName = sqlite3_column_name(stmt, i);
    int type = sqlite3_column_type(stmt, i);

   id value = [NSNull null];
   if(type == SQLITE_INTEGER) {
      value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)];
   } else if (type == SQLITE_TEXT) {
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
 for(int i=0; i<columnCount; i++) {
    //0-based index!
    const char * colName = sqlite3_column_name(stmt, i);
    int type = sqlite3_column_type(stmt, i);

   id value = [NSNull null];
   if(type == SQLITE_INTEGER) {
      value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)];
   } else if (type == SQLITE_TEXT) {
      const unsigned char * text = sqlite3_column_text(stmt, i);
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
 for(int i=0; i<columnCount; i++) {
    //0-based index!
    const char * colName = sqlite3_column_name(stmt, i);
    int type = sqlite3_column_type(stmt, i);

   id value = [NSNull null];
   if(type == SQLITE_INTEGER) {
      value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)];
   } else if (type == SQLITE_TEXT) {
      const unsigned char * text = sqlite3_column_text(stmt, i);
      value = [NSString stringWithFormat:@"%s", text];
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
 for(int i=0; i<columnCount; i++) {
    //0-based index!
    const char * colName = sqlite3_column_name(stmt, i);
    int type = sqlite3_column_type(stmt, i);

   id value = [NSNull null];
   if(type == SQLITE_INTEGER) {
      value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)];
   } else if (type == SQLITE_TEXT) {
      const unsigned char * text = sqlite3_column_text(stmt, i);
      value = [NSString stringWithFormat:@"%s", text];
   } else {
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
 for(int i=0; i<columnCount; i++) {
    //0-based index!
    const char * colName = sqlite3_column_name(stmt, i);
    int type = sqlite3_column_type(stmt, i);

   id value = [NSNull null];
   if(type == SQLITE_INTEGER) {
      value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)];
   } else if (type == SQLITE_TEXT) {
      const unsigned char * text = sqlite3_column_text(stmt, i);
      value = [NSString stringWithFormat:@"%s", text];
   } else {
      // more type handling
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
 for(int i=0; i<columnCount; i++) {
    //0-based index!
    const char * colName = sqlite3_column_name(stmt, i);
    int type = sqlite3_column_type(stmt, i);

   id value = [NSNull null];
   if(type == SQLITE_INTEGER) {
      value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)];
   } else if (type == SQLITE_TEXT) {
      const unsigned char * text = sqlite3_column_text(stmt, i);
      value = [NSString stringWithFormat:@"%s", text];
   } else {
      // more type handling
   }
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
 for(int i=0; i<columnCount; i++) {
    //0-based index!
    const char * colName = sqlite3_column_name(stmt, i);
    int type = sqlite3_column_type(stmt, i);

   id value = [NSNull null];
   if(type == SQLITE_INTEGER) {
      value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)];
   } else if (type == SQLITE_TEXT) {
      const unsigned char * text = sqlite3_column_text(stmt, i);
      value = [NSString stringWithFormat:@"%s", text];
   } else {
      // more type handling
   }
   [row setObject:value forKey:
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
 for(int i=0; i<columnCount; i++) {
    //0-based index!
    const char * colName = sqlite3_column_name(stmt, i);
    int type = sqlite3_column_type(stmt, i);

   id value = [NSNull null];
   if(type == SQLITE_INTEGER) {
      value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)];
   } else if (type == SQLITE_TEXT) {
      const unsigned char * text = sqlite3_column_text(stmt, i);
      value = [NSString stringWithFormat:@"%s", text];
   } else {
      // more type handling
   }
   [row setObject:value forKey:
       [NSString stringWithUTF8String:colName]];
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
 for(int i=0; i<columnCount; i++) {
    //0-based index!
    const char * colName = sqlite3_column_name(stmt, i);
    int type = sqlite3_column_type(stmt, i);

     id value = [NSNull null];
     if(type == SQLITE_INTEGER) {
        value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)];
     } else if (type == SQLITE_TEXT) {
        const unsigned char * text = sqlite3_column_text(stmt, i);
        value = [NSString stringWithFormat:@"%s", text];
     } else {
        // more type handling
     }
     [row setObject:value forKey:
         [NSString stringWithUTF8String:colName]];
 }
SQLite API-Prepared Statements


-(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt {
 int columnCount = sqlite3_column_count(stmt);
 for(int i=0; i<columnCount; i++) {
    //0-based index!
    const char * colName = sqlite3_column_name(stmt, i);
    int type = sqlite3_column_type(stmt, i);

        id value = [NSNull null];
        if(type == SQLITE_INTEGER) {
           value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)];
        } else if (type == SQLITE_TEXT) {
           const unsigned char * text = sqlite3_column_text(stmt, i);
           value = [NSString stringWithFormat:@"%s", text];
        } else {
           // more type handling
        }
        [row setObject:value forKey:
            [NSString stringWithUTF8String:colName]];
    }
}
FMDB


http://github.com/ccgus/fmdb
FMDB Usage
FMDB Usage
  FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
FMDB Usage
  FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
  if (![db open])
FMDB Usage
  FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
  if (![db open])
     NSLog(@"Could not open db.");
FMDB Usage
  FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
  if (![db open])
     NSLog(@"Could not open db.");

  [db executeUpdate:@"blah blah blah"];
FMDB Usage
  FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
  if (![db open])
     NSLog(@"Could not open db.");

  [db executeUpdate:@"blah blah blah"];
    
FMDB Usage
  FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
  if (![db open])
     NSLog(@"Could not open db.");

  [db executeUpdate:@"blah blah blah"];
    
  if ([db hadError])
FMDB Usage
  FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
  if (![db open])
     NSLog(@"Could not open db.");

  [db executeUpdate:@"blah blah blah"];
    
  if ([db hadError])
   NSLog(@"Err %d: %@", [db lastErrorCode],
FMDB Usage
  FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
  if (![db open])
     NSLog(@"Could not open db.");

  [db executeUpdate:@"blah blah blah"];
    
  if ([db hadError])
   NSLog(@"Err %d: %@", [db lastErrorCode],
       [db lastErrorMessage]);
FMDB Usage
  FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
  if (![db open])
     NSLog(@"Could not open db.");

  [db executeUpdate:@"blah blah blah"];
    
  if ([db hadError])
   NSLog(@"Err %d: %@", [db lastErrorCode],
       [db lastErrorMessage]);

  [db executeUpdate:@"create table test (a text, b integer)"];
FMDB Usage
  FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
  if (![db open])
     NSLog(@"Could not open db.");

  [db executeUpdate:@"blah blah blah"];
    
  if ([db hadError])
   NSLog(@"Err %d: %@", [db lastErrorCode],
       [db lastErrorMessage]);

  [db executeUpdate:@"create table test (a text, b integer)"];

  [db beginTransaction];
FMDB Usage
  FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
  if (![db open])
     NSLog(@"Could not open db.");

  [db executeUpdate:@"blah blah blah"];
    
  if ([db hadError])
   NSLog(@"Err %d: %@", [db lastErrorCode],
       [db lastErrorMessage]);

  [db executeUpdate:@"create table test (a text, b integer)"];

  [db beginTransaction];
  int i = 0;
FMDB Usage
  FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
  if (![db open])
     NSLog(@"Could not open db.");

  [db executeUpdate:@"blah blah blah"];
    
  if ([db hadError])
   NSLog(@"Err %d: %@", [db lastErrorCode],
       [db lastErrorMessage]);

  [db executeUpdate:@"create table test (a text, b integer)"];

  [db beginTransaction];
  int i = 0;
  while (i++ < 20) {
FMDB Usage
  FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
  if (![db open])
     NSLog(@"Could not open db.");

  [db executeUpdate:@"blah blah blah"];
    
  if ([db hadError])
   NSLog(@"Err %d: %@", [db lastErrorCode],
       [db lastErrorMessage]);

  [db executeUpdate:@"create table test (a text, b integer)"];

  [db beginTransaction];
  int i = 0;
  while (i++ < 20) {
    [db executeUpdate:@"insert into test (a, b) values (?, ?)" ,
FMDB Usage
  FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
  if (![db open])
     NSLog(@"Could not open db.");

  [db executeUpdate:@"blah blah blah"];
    
  if ([db hadError])
   NSLog(@"Err %d: %@", [db lastErrorCode],
       [db lastErrorMessage]);

  [db executeUpdate:@"create table test (a text, b integer)"];

  [db beginTransaction];
  int i = 0;
  while (i++ < 20) {
    [db executeUpdate:@"insert into test (a, b) values (?, ?)" ,
       @"hi'", // look! I put in a ', and I'm not escaping it!
FMDB Usage
  FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
  if (![db open])
     NSLog(@"Could not open db.");

  [db executeUpdate:@"blah blah blah"];
    
  if ([db hadError])
   NSLog(@"Err %d: %@", [db lastErrorCode],
       [db lastErrorMessage]);

  [db executeUpdate:@"create table test (a text, b integer)"];

  [db beginTransaction];
  int i = 0;
  while (i++ < 20) {
    [db executeUpdate:@"insert into test (a, b) values (?, ?)" ,
       @"hi'", // look! I put in a ', and I'm not escaping it!
       [NSNumber numberWithInt:i]];
FMDB Usage
  FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
  if (![db open])
     NSLog(@"Could not open db.");

  [db executeUpdate:@"blah blah blah"];
    
  if ([db hadError])
   NSLog(@"Err %d: %@", [db lastErrorCode],
       [db lastErrorMessage]);

  [db executeUpdate:@"create table test (a text, b integer)"];

  [db beginTransaction];
  int i = 0;
  while (i++ < 20) {
    [db executeUpdate:@"insert into test (a, b) values (?, ?)" ,
       @"hi'", // look! I put in a ', and I'm not escaping it!
       [NSNumber numberWithInt:i]];
  }
FMDB Usage
  FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
  if (![db open])
     NSLog(@"Could not open db.");

  [db executeUpdate:@"blah blah blah"];
    
  if ([db hadError])
   NSLog(@"Err %d: %@", [db lastErrorCode],
       [db lastErrorMessage]);

  [db executeUpdate:@"create table test (a text, b integer)"];

  [db beginTransaction];
  int i = 0;
  while (i++ < 20) {
    [db executeUpdate:@"insert into test (a, b) values (?, ?)" ,
       @"hi'", // look! I put in a ', and I'm not escaping it!
       [NSNumber numberWithInt:i]];
  }
  [db commit];
What about updates?


Database is never copied over again

If we did, your customers would lose
data
Migrations

Concept from Ruby on Rails

Evolutionary database design

Each database has a "version"

App runs all migrations to the latest
version
Migrations

number        name
  1      initial_schema
  2        update_foo
  3      implement_blah
Migrations

number        name
  1      initial_schema
  2        update_foo
  3      implement_blah
Hand-rolled Database
      Migrator


 DEMO
FMDB Migration
       Manager


http://github.com/mocra/fmdb-
migration-manager
FMDB Migration
                  Manager

NSArray *migrations = [NSArray arrayWithObjects:
     [CreateStudents migration], // 1
     [CreateStaffMembers migration], // 2
     [AddStudentNumberToStudents migration], // 3
     nil
  ];
[FmdbMigrationManager executeForDatabasePath:@"/tmp/tmp.db"
withMigrations:migrations];
FMDB Migration
                      Manager
@interface CreateStudents : FmdbMigration
{
}
@end

@implementation CreateStudents
- (void)up {
  [self createTable:@"students" withColumns:[NSArray arrayWithObjects:
      [FmdbMigrationColumn columnWithColumnName:@"first_name" columnType:@"string"],
      [FmdbMigrationColumn columnWithColumnName:@"age" columnType:@"integer"
             defaultValue:21], nil];
}

- (void)down {
  [self dropTable:@"students"];
}

@end
ActiveRecord

Person *p = [[Person alloc] init];
p.name = @"Joe";
p.occupation = @"Dishwasher";

[p save];




Person *p = [Person personWithId:5];
==> Person {id:5, name:Joe, occupation:Dishwasher}
Aptiva's ActiveRecord


 http://github.com/aptiva/activerecord




                (YMMV)
Questions?

Más contenido relacionado

La actualidad más candente

PHP 5.3 Overview
PHP 5.3 OverviewPHP 5.3 Overview
PHP 5.3 Overview
jsmith92
 
2005_Structures and functions of Makefile
2005_Structures and functions of Makefile2005_Structures and functions of Makefile
2005_Structures and functions of Makefile
NakCheon Jung
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 World
Fabien Potencier
 

La actualidad más candente (20)

PHP 5.3 Overview
PHP 5.3 OverviewPHP 5.3 Overview
PHP 5.3 Overview
 
Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6
 
extending-php
extending-phpextending-php
extending-php
 
Pragmatic sbt
Pragmatic sbtPragmatic sbt
Pragmatic sbt
 
2005_Structures and functions of Makefile
2005_Structures and functions of Makefile2005_Structures and functions of Makefile
2005_Structures and functions of Makefile
 
Symfony 4 & Flex news
Symfony 4 & Flex newsSymfony 4 & Flex news
Symfony 4 & Flex news
 
Common mistakes made with Functional Java
Common mistakes made with Functional JavaCommon mistakes made with Functional Java
Common mistakes made with Functional Java
 
Common mistakes functional java devoxx
Common mistakes functional java devoxxCommon mistakes functional java devoxx
Common mistakes functional java devoxx
 
The Origin of Lithium
The Origin of LithiumThe Origin of Lithium
The Origin of Lithium
 
SPL, not a bridge too far
SPL, not a bridge too farSPL, not a bridge too far
SPL, not a bridge too far
 
Ten common mistakes made with Functional Java JBCNConf18
Ten common mistakes made with Functional Java JBCNConf18Ten common mistakes made with Functional Java JBCNConf18
Ten common mistakes made with Functional Java JBCNConf18
 
Common mistakes functional java vjug
Common mistakes functional java vjugCommon mistakes functional java vjug
Common mistakes functional java vjug
 
Common mistakes functional java | Oracle Code One 2018
Common mistakes functional java | Oracle Code One 2018Common mistakes functional java | Oracle Code One 2018
Common mistakes functional java | Oracle Code One 2018
 
Codeigniter4の比較と検証
Codeigniter4の比較と検証Codeigniter4の比較と検証
Codeigniter4の比較と検証
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 World
 
Php MySql For Beginners
Php MySql For BeginnersPhp MySql For Beginners
Php MySql For Beginners
 
Lithium Best
Lithium Best Lithium Best
Lithium Best
 
Perl 1997 Perl As A System Glue
Perl 1997 Perl As A System GluePerl 1997 Perl As A System Glue
Perl 1997 Perl As A System Glue
 
TRunner
TRunnerTRunner
TRunner
 
Reactive Access to MongoDB from Scala
Reactive Access to MongoDB from ScalaReactive Access to MongoDB from Scala
Reactive Access to MongoDB from Scala
 

Similar a SQLite Techniques

HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
Dmitry Soshnikov
 
Web2py Code Lab
Web2py Code LabWeb2py Code Lab
Web2py Code Lab
Colin Su
 
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docxWeb CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
celenarouzie
 
From HelloWorld to Configurable and Reusable Apache Spark Applications in Sca...
From HelloWorld to Configurable and Reusable Apache Spark Applications in Sca...From HelloWorld to Configurable and Reusable Apache Spark Applications in Sca...
From HelloWorld to Configurable and Reusable Apache Spark Applications in Sca...
Databricks
 
Introduction to the New Asynchronous API in the .NET Driver
Introduction to the New Asynchronous API in the .NET DriverIntroduction to the New Asynchronous API in the .NET Driver
Introduction to the New Asynchronous API in the .NET Driver
MongoDB
 

Similar a SQLite Techniques (20)

I Phone On Rails
I Phone On RailsI Phone On Rails
I Phone On Rails
 
3 database-jdbc(1)
3 database-jdbc(1)3 database-jdbc(1)
3 database-jdbc(1)
 
Ruby on Rails: Tasty Burgers
Ruby on Rails: Tasty BurgersRuby on Rails: Tasty Burgers
Ruby on Rails: Tasty Burgers
 
Laravel dokumentacja Restful API - swagger
Laravel dokumentacja Restful API - swaggerLaravel dokumentacja Restful API - swagger
Laravel dokumentacja Restful API - swagger
 
Intoduction on Playframework
Intoduction on PlayframeworkIntoduction on Playframework
Intoduction on Playframework
 
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
 
Web2py Code Lab
Web2py Code LabWeb2py Code Lab
Web2py Code Lab
 
Building Lithium Apps
Building Lithium AppsBuilding Lithium Apps
Building Lithium Apps
 
Intorduction of Playframework
Intorduction of PlayframeworkIntorduction of Playframework
Intorduction of Playframework
 
Java 8: the good parts!
Java 8: the good parts!Java 8: the good parts!
Java 8: the good parts!
 
JavaOne 2013: Java 8 - The Good Parts
JavaOne 2013: Java 8 - The Good PartsJavaOne 2013: Java 8 - The Good Parts
JavaOne 2013: Java 8 - The Good Parts
 
Spring boot
Spring boot Spring boot
Spring boot
 
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docxWeb CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
 
Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5
 
Having Fun Programming!
Having Fun Programming!Having Fun Programming!
Having Fun Programming!
 
Twitter codeigniter library
Twitter codeigniter libraryTwitter codeigniter library
Twitter codeigniter library
 
PHP Workshop Notes
PHP Workshop NotesPHP Workshop Notes
PHP Workshop Notes
 
Solr @ Etsy - Apache Lucene Eurocon
Solr @ Etsy - Apache Lucene EuroconSolr @ Etsy - Apache Lucene Eurocon
Solr @ Etsy - Apache Lucene Eurocon
 
From HelloWorld to Configurable and Reusable Apache Spark Applications in Sca...
From HelloWorld to Configurable and Reusable Apache Spark Applications in Sca...From HelloWorld to Configurable and Reusable Apache Spark Applications in Sca...
From HelloWorld to Configurable and Reusable Apache Spark Applications in Sca...
 
Introduction to the New Asynchronous API in the .NET Driver
Introduction to the New Asynchronous API in the .NET DriverIntroduction to the New Asynchronous API in the .NET Driver
Introduction to the New Asynchronous API in the .NET Driver
 

Más de Ben Scheirman (6)

Meet Git
Meet GitMeet Git
Meet Git
 
Effective iOS Network Programming Techniques
Effective iOS Network Programming TechniquesEffective iOS Network Programming Techniques
Effective iOS Network Programming Techniques
 
Objective-C & iPhone for .NET Developers
Objective-C & iPhone for .NET DevelopersObjective-C & iPhone for .NET Developers
Objective-C & iPhone for .NET Developers
 
iPhone for .NET Developers
iPhone for .NET DevelopersiPhone for .NET Developers
iPhone for .NET Developers
 
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 SecondsA Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
 
Reasons To Love Ruby
Reasons To Love RubyReasons To Love Ruby
Reasons To Love Ruby
 

SQLite Techniques

  • 1. SQLite Techniques Ben Scheirman @subdigital Code / Slides: github.com/subdigital/iphonedevcon- sandiego
  • 3. SQLite API Pure C API Lots of strange-looking, hard to debug code Best choice is to abstract it away
  • 4. SQLite Tools SQLite Manager Add-in for Firefox sqlite3 command line utility
  • 5. Writable Databases Your app bundle is signed! (That means the contents can't change)
  • 6. Your App Sandbox Your App Bundle Resources Documents
  • 7. Your App Sandbox Your App Bundle Resources Documents
  • 8. Creating the database
  • 10. Getting Resource Paths //fetches
path
for
foo.db
  • 11. Getting Resource Paths //fetches
path
for
foo.db NSString
*resourcePath
=
[[NSBundle
mainBundle]
  • 12. Getting Resource Paths //fetches
path
for
foo.db NSString
*resourcePath
=
[[NSBundle
mainBundle] 




























pathForResource:@"foo"
  • 13. Getting Resource Paths //fetches
path
for
foo.db NSString
*resourcePath
=
[[NSBundle
mainBundle] 




























pathForResource:@"foo" 




























ofType:@"db"];
  • 15. Finding the Documents Directory NSArray
*paths
=
NSSearchPathForDirectoriesInDomains(

  • 16. Finding the Documents Directory NSArray
*paths
=
NSSearchPathForDirectoriesInDomains(
 


























NSDocumentDirectory,

  • 17. Finding the Documents Directory NSArray
*paths
=
NSSearchPathForDirectoriesInDomains(
 


























NSDocumentDirectory,
 


























NSUserDomainMask,

  • 18. Finding the Documents Directory NSArray
*paths
=
NSSearchPathForDirectoriesInDomains(
 


























NSDocumentDirectory,
 


























NSUserDomainMask,
 


























YES);
  • 19. Finding the Documents Directory NSArray
*paths
=
NSSearchPathForDirectoriesInDomains(
 


























NSDocumentDirectory,
 


























NSUserDomainMask,
 


























YES); NSString
*documentsDirectory
=
[paths
objectAtIndex:0];
  • 21. Copy a default database on startup //in applicationDidFinishLaunching NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"app" ofType:@"db"]; NSString *targetPath = ... NSFileManager *fm = [NSFileManager defaultManager]; if(![fm fileExistsAtPath:targetPath]) { [fm copyItemAtPath:sourcePath toPath:targetPath error:&error]; }
  • 22. Copy a default database on startup //in applicationDidFinishLaunching NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"app" ofType:@"db"]; NSString *targetPath = ... NSFileManager *fm = [NSFileManager defaultManager]; if(![fm fileExistsAtPath:targetPath]) { [fm copyItemAtPath:sourcePath toPath:targetPath error:&error]; }
  • 23. Copy a default database on startup //in applicationDidFinishLaunching NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"app" ofType:@"db"]; NSString *targetPath = ... NSFileManager *fm = [NSFileManager defaultManager]; if(![fm fileExistsAtPath:targetPath]) { [fm copyItemAtPath:sourcePath toPath:targetPath error:&error]; }
  • 24. Copy a default database on startup //in applicationDidFinishLaunching NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"app" ofType:@"db"]; NSString *targetPath = ... NSFileManager *fm = [NSFileManager defaultManager]; if(![fm fileExistsAtPath:targetPath]) { [fm copyItemAtPath:sourcePath toPath:targetPath error:&error]; }
  • 25. Copy a default database on startup //in applicationDidFinishLaunching NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"app" ofType:@"db"]; NSString *targetPath = ... NSFileManager *fm = [NSFileManager defaultManager]; if(![fm fileExistsAtPath:targetPath]) { [fm copyItemAtPath:sourcePath toPath:targetPath error:&error]; }
  • 26. Copy a default database on startup //in applicationDidFinishLaunching NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"app" ofType:@"db"]; NSString *targetPath = ... NSFileManager *fm = [NSFileManager defaultManager]; if(![fm fileExistsAtPath:targetPath]) { [fm copyItemAtPath:sourcePath toPath:targetPath error:&error]; }
  • 27. Copy a default database on startup //in applicationDidFinishLaunching NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"app" ofType:@"db"]; NSString *targetPath = ... NSFileManager *fm = [NSFileManager defaultManager]; if(![fm fileExistsAtPath:targetPath]) { [fm copyItemAtPath:sourcePath toPath:targetPath error:&error]; }
  • 33. SQLite API - Opening a connection
  • 34. SQLite API - Opening a connection #import "sqlite3.h"
  • 35. SQLite API - Opening a connection #import "sqlite3.h" sqlite3 *db;
  • 36. SQLite API - Opening a connection #import "sqlite3.h" sqlite3 *db; int result = sqlite3_open(PATH, &db);
  • 37. SQLite API - Opening a connection #import "sqlite3.h" sqlite3 *db; int result = sqlite3_open(PATH, &db); if (result != SQLITE_OK) {
  • 38. SQLite API - Opening a connection #import "sqlite3.h" sqlite3 *db; int result = sqlite3_open(PATH, &db); if (result != SQLITE_OK) { [NSException raise:@"SQLITE ERROR"
  • 39. SQLite API - Opening a connection #import "sqlite3.h" sqlite3 *db; int result = sqlite3_open(PATH, &db); if (result != SQLITE_OK) { [NSException raise:@"SQLITE ERROR" format:@"Error %d", result];
  • 40. SQLite API - Opening a connection #import "sqlite3.h" sqlite3 *db; int result = sqlite3_open(PATH, &db); if (result != SQLITE_OK) { [NSException raise:@"SQLITE ERROR" format:@"Error %d", result]; }
  • 41. SQLite API - Opening a connection #import "sqlite3.h" sqlite3 *db; int result = sqlite3_open(PATH, &db); if (result != SQLITE_OK) { [NSException raise:@"SQLITE ERROR" format:@"Error %d", result]; } //later
  • 42. SQLite API - Opening a connection #import "sqlite3.h" sqlite3 *db; int result = sqlite3_open(PATH, &db); if (result != SQLITE_OK) { [NSException raise:@"SQLITE ERROR" format:@"Error %d", result]; } //later sqlite3_close(db);
  • 44. SQLite API - executing queries int sqlite3_exec(
  • 45. SQLite API - executing queries int sqlite3_exec( sqlite3 *db,
  • 46. SQLite API - executing queries int sqlite3_exec( sqlite3 *db, char *sql,
  • 47. SQLite API - executing queries int sqlite3_exec( sqlite3 *db, char *sql, int (*callback)(void *, int, char**, char**) callbackFunc,
  • 48. SQLite API - executing queries int sqlite3_exec( sqlite3 *db, char *sql, int (*callback)(void *, int, char**, char**) callbackFunc, void *context,
  • 49. SQLite API - executing queries int sqlite3_exec( sqlite3 *db, char *sql, int (*callback)(void *, int, char**, char**) callbackFunc, void *context, char **errorMessage
  • 50. SQLite API - executing queries int sqlite3_exec( sqlite3 *db, char *sql, int (*callback)(void *, int, char**, char**) callbackFunc, void *context, char **errorMessage );
  • 51. SQLite API - executing queries int sqlite3_exec( sqlite3 *db, char *sql, int (*callback)(void *, int, char**, char**) callbackFunc, void *context, char **errorMessage ); //callback signature
  • 52. SQLite API - executing queries int sqlite3_exec( sqlite3 *db, char *sql, int (*callback)(void *, int, char**, char**) callbackFunc, void *context, char **errorMessage ); //callback signature int RowCallback(void * context,
  • 53. SQLite API - executing queries int sqlite3_exec( sqlite3 *db, char *sql, int (*callback)(void *, int, char**, char**) callbackFunc, void *context, char **errorMessage ); //callback signature int RowCallback(void * context, int numColumns,
  • 54. SQLite API - executing queries int sqlite3_exec( sqlite3 *db, char *sql, int (*callback)(void *, int, char**, char**) callbackFunc, void *context, char **errorMessage ); //callback signature int RowCallback(void * context, int numColumns, char** colValues,
  • 55. SQLite API - executing queries int sqlite3_exec( sqlite3 *db, char *sql, int (*callback)(void *, int, char**, char**) callbackFunc, void *context, char **errorMessage ); //callback signature int RowCallback(void * context, int numColumns, char** colValues, char ** colNames);
  • 56. SQLite API - Prepared Statements More Powerful Unfortunately much more code!
  • 59. SQLite API-Prepared Statements sqlite3 *db; int result = sqlite3_open(DB_PATH, &db);
  • 60. SQLite API-Prepared Statements sqlite3 *db; int result = sqlite3_open(DB_PATH, &db); if(result != SQLITE_OK) {
  • 61. SQLite API-Prepared Statements sqlite3 *db; int result = sqlite3_open(DB_PATH, &db); if(result != SQLITE_OK) { //handle error
  • 62. SQLite API-Prepared Statements sqlite3 *db; int result = sqlite3_open(DB_PATH, &db); if(result != SQLITE_OK) { //handle error }
  • 63. SQLite API-Prepared Statements sqlite3 *db; int result = sqlite3_open(DB_PATH, &db); if(result != SQLITE_OK) { //handle error }
  • 64. SQLite API-Prepared Statements sqlite3 *db; int result = sqlite3_open(DB_PATH, &db); if(result != SQLITE_OK) { //handle error } sqlite3_stmt *stmt;
  • 65. SQLite API-Prepared Statements sqlite3 *db; int result = sqlite3_open(DB_PATH, &db); if(result != SQLITE_OK) { //handle error } sqlite3_stmt *stmt; result = sqlite3_prepare_v2(db, sql_cstring, -1, &stmt, NULL);
  • 66. SQLite API-Prepared Statements sqlite3 *db; int result = sqlite3_open(DB_PATH, &db); if(result != SQLITE_OK) { //handle error } sqlite3_stmt *stmt; result = sqlite3_prepare_v2(db, sql_cstring, -1, &stmt, NULL); if(result != SQLITE_OK) {
  • 67. SQLite API-Prepared Statements sqlite3 *db; int result = sqlite3_open(DB_PATH, &db); if(result != SQLITE_OK) { //handle error } sqlite3_stmt *stmt; result = sqlite3_prepare_v2(db, sql_cstring, -1, &stmt, NULL); if(result != SQLITE_OK) { //handle error
  • 68. SQLite API-Prepared Statements sqlite3 *db; int result = sqlite3_open(DB_PATH, &db); if(result != SQLITE_OK) { //handle error } sqlite3_stmt *stmt; result = sqlite3_prepare_v2(db, sql_cstring, -1, &stmt, NULL); if(result != SQLITE_OK) { //handle error }
  • 69. SQLite API-Prepared Statements sqlite3 *db; int result = sqlite3_open(DB_PATH, &db); if(result != SQLITE_OK) { //handle error } sqlite3_stmt *stmt; result = sqlite3_prepare_v2(db, sql_cstring, -1, &stmt, NULL); if(result != SQLITE_OK) { //handle error } //...
  • 70. SQLite API-Prepared Statements sqlite3 *db; int result = sqlite3_open(DB_PATH, &db); if(result != SQLITE_OK) { //handle error } sqlite3_stmt *stmt; result = sqlite3_prepare_v2(db, sql_cstring, -1, &stmt, NULL); if(result != SQLITE_OK) { //handle error } //...
  • 71. SQLite API-Prepared Statements sqlite3 *db; int result = sqlite3_open(DB_PATH, &db); if(result != SQLITE_OK) { //handle error } sqlite3_stmt *stmt; result = sqlite3_prepare_v2(db, sql_cstring, -1, &stmt, NULL); if(result != SQLITE_OK) { //handle error } //...
  • 72. SQLite API-Prepared Statements sqlite3 *db; int result = sqlite3_open(DB_PATH, &db); if(result != SQLITE_OK) { //handle error } sqlite3_stmt *stmt; result = sqlite3_prepare_v2(db, sql_cstring, -1, &stmt, NULL); if(result != SQLITE_OK) { //handle error } //...
  • 74. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?)
  • 75. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX!
  • 76. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5);
  • 77. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5); sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);
  • 78. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5); sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT);
  • 79. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5); sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT); result = sqlite3_step(stmt);
  • 80. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5); sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT); result = sqlite3_step(stmt); if(result == SQLITE_DONE)
  • 81. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5); sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT); result = sqlite3_step(stmt); if(result == SQLITE_DONE) return [NSArray array]; //nothing to do!
  • 82. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5); sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT); result = sqlite3_step(stmt); if(result == SQLITE_DONE) return [NSArray array]; //nothing to do!
  • 83. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5); sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT); result = sqlite3_step(stmt); if(result == SQLITE_DONE) return [NSArray array]; //nothing to do! NSMutableArray *results = [NSMutableArray array];
  • 84. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5); sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT); result = sqlite3_step(stmt); if(result == SQLITE_DONE) return [NSArray array]; //nothing to do! NSMutableArray *results = [NSMutableArray array]; while(result == SQLITE_ROW) {
  • 85. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5); sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT); result = sqlite3_step(stmt); if(result == SQLITE_DONE) return [NSArray array]; //nothing to do! NSMutableArray *results = [NSMutableArray array]; while(result == SQLITE_ROW) { NSMutableDictionary *row = [NSMutableDictionary dictionary];
  • 86. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5); sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT); result = sqlite3_step(stmt); if(result == SQLITE_DONE) return [NSArray array]; //nothing to do! NSMutableArray *results = [NSMutableArray array]; while(result == SQLITE_ROW) { NSMutableDictionary *row = [NSMutableDictionary dictionary];
  • 87. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5); sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT); result = sqlite3_step(stmt); if(result == SQLITE_DONE) return [NSArray array]; //nothing to do! NSMutableArray *results = [NSMutableArray array]; while(result == SQLITE_ROW) { NSMutableDictionary *row = [NSMutableDictionary dictionary]; [self processRow:row forStatement:stmt];
  • 88. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5); sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT); result = sqlite3_step(stmt); if(result == SQLITE_DONE) return [NSArray array]; //nothing to do! NSMutableArray *results = [NSMutableArray array]; while(result == SQLITE_ROW) { NSMutableDictionary *row = [NSMutableDictionary dictionary]; [self processRow:row forStatement:stmt]; [results addObject:row];
  • 89. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5); sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT); result = sqlite3_step(stmt); if(result == SQLITE_DONE) return [NSArray array]; //nothing to do! NSMutableArray *results = [NSMutableArray array]; while(result == SQLITE_ROW) { NSMutableDictionary *row = [NSMutableDictionary dictionary]; [self processRow:row forStatement:stmt]; [results addObject:row]; result = sqlite3_step(stmt);
  • 90. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5); sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT); result = sqlite3_step(stmt); if(result == SQLITE_DONE) return [NSArray array]; //nothing to do! NSMutableArray *results = [NSMutableArray array]; while(result == SQLITE_ROW) { NSMutableDictionary *row = [NSMutableDictionary dictionary]; [self processRow:row forStatement:stmt]; [results addObject:row]; result = sqlite3_step(stmt); }
  • 91. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5); sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT); result = sqlite3_step(stmt); if(result == SQLITE_DONE) return [NSArray array]; //nothing to do! NSMutableArray *results = [NSMutableArray array]; while(result == SQLITE_ROW) { NSMutableDictionary *row = [NSMutableDictionary dictionary]; [self processRow:row forStatement:stmt]; [results addObject:row]; result = sqlite3_step(stmt); } sqlite_finalize(stmt);
  • 92. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5); sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT); result = sqlite3_step(stmt); if(result == SQLITE_DONE) return [NSArray array]; //nothing to do! NSMutableArray *results = [NSMutableArray array]; while(result == SQLITE_ROW) { NSMutableDictionary *row = [NSMutableDictionary dictionary]; [self processRow:row forStatement:stmt]; [results addObject:row]; result = sqlite3_step(stmt); } sqlite_finalize(stmt);
  • 93. SQLite API-Prepared Statements //INSERT INTO CARS(year, make) VALUES(?, ?) //PARAMETERS HAVE A 1-BASED INDEX! sqlite3_bind_int(stmt, 1 /* idx */, 5); sqlite3_bind_text(stmt, 2 /* idx */,"value", -1, SQLITE_TRANSIENT); result = sqlite3_step(stmt); if(result == SQLITE_DONE) return [NSArray array]; //nothing to do! NSMutableArray *results = [NSMutableArray array]; while(result == SQLITE_ROW) { NSMutableDictionary *row = [NSMutableDictionary dictionary]; [self processRow:row forStatement:stmt]; [results addObject:row]; result = sqlite3_step(stmt); } sqlite_finalize(stmt); return results;
  • 96. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt);
  • 97. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt); for(int i=0; i<columnCount; i++) {
  • 98. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt); for(int i=0; i<columnCount; i++) { //0-based index!
  • 99. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt); for(int i=0; i<columnCount; i++) { //0-based index! const char * colName = sqlite3_column_name(stmt, i);
  • 100. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt); for(int i=0; i<columnCount; i++) { //0-based index! const char * colName = sqlite3_column_name(stmt, i); int type = sqlite3_column_type(stmt, i);
  • 101. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt); for(int i=0; i<columnCount; i++) { //0-based index! const char * colName = sqlite3_column_name(stmt, i); int type = sqlite3_column_type(stmt, i); id value = [NSNull null];
  • 102. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt); for(int i=0; i<columnCount; i++) { //0-based index! const char * colName = sqlite3_column_name(stmt, i); int type = sqlite3_column_type(stmt, i); id value = [NSNull null]; if(type == SQLITE_INTEGER) {
  • 103. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt); for(int i=0; i<columnCount; i++) { //0-based index! const char * colName = sqlite3_column_name(stmt, i); int type = sqlite3_column_type(stmt, i); id value = [NSNull null]; if(type == SQLITE_INTEGER) { value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)];
  • 104. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt); for(int i=0; i<columnCount; i++) { //0-based index! const char * colName = sqlite3_column_name(stmt, i); int type = sqlite3_column_type(stmt, i); id value = [NSNull null]; if(type == SQLITE_INTEGER) { value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)]; } else if (type == SQLITE_TEXT) {
  • 105. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt); for(int i=0; i<columnCount; i++) { //0-based index! const char * colName = sqlite3_column_name(stmt, i); int type = sqlite3_column_type(stmt, i); id value = [NSNull null]; if(type == SQLITE_INTEGER) { value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)]; } else if (type == SQLITE_TEXT) { const unsigned char * text = sqlite3_column_text(stmt, i);
  • 106. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt); for(int i=0; i<columnCount; i++) { //0-based index! const char * colName = sqlite3_column_name(stmt, i); int type = sqlite3_column_type(stmt, i); id value = [NSNull null]; if(type == SQLITE_INTEGER) { value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)]; } else if (type == SQLITE_TEXT) { const unsigned char * text = sqlite3_column_text(stmt, i); value = [NSString stringWithFormat:@"%s", text];
  • 107. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt); for(int i=0; i<columnCount; i++) { //0-based index! const char * colName = sqlite3_column_name(stmt, i); int type = sqlite3_column_type(stmt, i); id value = [NSNull null]; if(type == SQLITE_INTEGER) { value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)]; } else if (type == SQLITE_TEXT) { const unsigned char * text = sqlite3_column_text(stmt, i); value = [NSString stringWithFormat:@"%s", text]; } else {
  • 108. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt); for(int i=0; i<columnCount; i++) { //0-based index! const char * colName = sqlite3_column_name(stmt, i); int type = sqlite3_column_type(stmt, i); id value = [NSNull null]; if(type == SQLITE_INTEGER) { value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)]; } else if (type == SQLITE_TEXT) { const unsigned char * text = sqlite3_column_text(stmt, i); value = [NSString stringWithFormat:@"%s", text]; } else { // more type handling
  • 109. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt); for(int i=0; i<columnCount; i++) { //0-based index! const char * colName = sqlite3_column_name(stmt, i); int type = sqlite3_column_type(stmt, i); id value = [NSNull null]; if(type == SQLITE_INTEGER) { value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)]; } else if (type == SQLITE_TEXT) { const unsigned char * text = sqlite3_column_text(stmt, i); value = [NSString stringWithFormat:@"%s", text]; } else { // more type handling }
  • 110. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt); for(int i=0; i<columnCount; i++) { //0-based index! const char * colName = sqlite3_column_name(stmt, i); int type = sqlite3_column_type(stmt, i); id value = [NSNull null]; if(type == SQLITE_INTEGER) { value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)]; } else if (type == SQLITE_TEXT) { const unsigned char * text = sqlite3_column_text(stmt, i); value = [NSString stringWithFormat:@"%s", text]; } else { // more type handling } [row setObject:value forKey:
  • 111. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt); for(int i=0; i<columnCount; i++) { //0-based index! const char * colName = sqlite3_column_name(stmt, i); int type = sqlite3_column_type(stmt, i); id value = [NSNull null]; if(type == SQLITE_INTEGER) { value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)]; } else if (type == SQLITE_TEXT) { const unsigned char * text = sqlite3_column_text(stmt, i); value = [NSString stringWithFormat:@"%s", text]; } else { // more type handling } [row setObject:value forKey: [NSString stringWithUTF8String:colName]];
  • 112. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt); for(int i=0; i<columnCount; i++) { //0-based index! const char * colName = sqlite3_column_name(stmt, i); int type = sqlite3_column_type(stmt, i); id value = [NSNull null]; if(type == SQLITE_INTEGER) { value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)]; } else if (type == SQLITE_TEXT) { const unsigned char * text = sqlite3_column_text(stmt, i); value = [NSString stringWithFormat:@"%s", text]; } else { // more type handling } [row setObject:value forKey: [NSString stringWithUTF8String:colName]]; }
  • 113. SQLite API-Prepared Statements -(void)processRow:(NSMutableDictionary *)row forStatement:(sqlite3_stmt*)stmt { int columnCount = sqlite3_column_count(stmt); for(int i=0; i<columnCount; i++) { //0-based index! const char * colName = sqlite3_column_name(stmt, i); int type = sqlite3_column_type(stmt, i); id value = [NSNull null]; if(type == SQLITE_INTEGER) { value = [NSNumber numberWithInt:sqlite3_column_int(stmt, i)]; } else if (type == SQLITE_TEXT) { const unsigned char * text = sqlite3_column_text(stmt, i); value = [NSString stringWithFormat:@"%s", text]; } else { // more type handling } [row setObject:value forKey: [NSString stringWithUTF8String:colName]]; } }
  • 116. FMDB Usage   FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
  • 117. FMDB Usage   FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];   if (![db open])
  • 118. FMDB Usage   FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];   if (![db open])      NSLog(@"Could not open db.");
  • 119. FMDB Usage   FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];   if (![db open])      NSLog(@"Could not open db.");   [db executeUpdate:@"blah blah blah"];
  • 120. FMDB Usage   FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];   if (![db open])      NSLog(@"Could not open db.");   [db executeUpdate:@"blah blah blah"];     
  • 121. FMDB Usage   FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];   if (![db open])      NSLog(@"Could not open db.");   [db executeUpdate:@"blah blah blah"];        if ([db hadError])
  • 122. FMDB Usage   FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];   if (![db open])      NSLog(@"Could not open db.");   [db executeUpdate:@"blah blah blah"];        if ([db hadError])    NSLog(@"Err %d: %@", [db lastErrorCode],
  • 123. FMDB Usage   FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];   if (![db open])      NSLog(@"Could not open db.");   [db executeUpdate:@"blah blah blah"];        if ([db hadError])    NSLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);
  • 124. FMDB Usage   FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];   if (![db open])      NSLog(@"Could not open db.");   [db executeUpdate:@"blah blah blah"];        if ([db hadError])    NSLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);   [db executeUpdate:@"create table test (a text, b integer)"];
  • 125. FMDB Usage   FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];   if (![db open])      NSLog(@"Could not open db.");   [db executeUpdate:@"blah blah blah"];        if ([db hadError])    NSLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);   [db executeUpdate:@"create table test (a text, b integer)"];   [db beginTransaction];
  • 126. FMDB Usage   FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];   if (![db open])      NSLog(@"Could not open db.");   [db executeUpdate:@"blah blah blah"];        if ([db hadError])    NSLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);   [db executeUpdate:@"create table test (a text, b integer)"];   [db beginTransaction];   int i = 0;
  • 127. FMDB Usage   FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];   if (![db open])      NSLog(@"Could not open db.");   [db executeUpdate:@"blah blah blah"];        if ([db hadError])    NSLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);   [db executeUpdate:@"create table test (a text, b integer)"];   [db beginTransaction];   int i = 0;   while (i++ < 20) {
  • 128. FMDB Usage   FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];   if (![db open])      NSLog(@"Could not open db.");   [db executeUpdate:@"blah blah blah"];        if ([db hadError])    NSLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);   [db executeUpdate:@"create table test (a text, b integer)"];   [db beginTransaction];   int i = 0;   while (i++ < 20) {   [db executeUpdate:@"insert into test (a, b) values (?, ?)" ,
  • 129. FMDB Usage   FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];   if (![db open])      NSLog(@"Could not open db.");   [db executeUpdate:@"blah blah blah"];        if ([db hadError])    NSLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);   [db executeUpdate:@"create table test (a text, b integer)"];   [db beginTransaction];   int i = 0;   while (i++ < 20) {   [db executeUpdate:@"insert into test (a, b) values (?, ?)" ,        @"hi'", // look! I put in a ', and I'm not escaping it!
  • 130. FMDB Usage   FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];   if (![db open])      NSLog(@"Could not open db.");   [db executeUpdate:@"blah blah blah"];        if ([db hadError])    NSLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);   [db executeUpdate:@"create table test (a text, b integer)"];   [db beginTransaction];   int i = 0;   while (i++ < 20) {   [db executeUpdate:@"insert into test (a, b) values (?, ?)" ,        @"hi'", // look! I put in a ', and I'm not escaping it!        [NSNumber numberWithInt:i]];
  • 131. FMDB Usage   FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];   if (![db open])      NSLog(@"Could not open db.");   [db executeUpdate:@"blah blah blah"];        if ([db hadError])    NSLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);   [db executeUpdate:@"create table test (a text, b integer)"];   [db beginTransaction];   int i = 0;   while (i++ < 20) {   [db executeUpdate:@"insert into test (a, b) values (?, ?)" ,        @"hi'", // look! I put in a ', and I'm not escaping it!        [NSNumber numberWithInt:i]];   }
  • 132. FMDB Usage   FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];   if (![db open])      NSLog(@"Could not open db.");   [db executeUpdate:@"blah blah blah"];        if ([db hadError])    NSLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);   [db executeUpdate:@"create table test (a text, b integer)"];   [db beginTransaction];   int i = 0;   while (i++ < 20) {   [db executeUpdate:@"insert into test (a, b) values (?, ?)" ,        @"hi'", // look! I put in a ', and I'm not escaping it!        [NSNumber numberWithInt:i]];   }   [db commit];
  • 133. What about updates? Database is never copied over again If we did, your customers would lose data
  • 134. Migrations Concept from Ruby on Rails Evolutionary database design Each database has a "version" App runs all migrations to the latest version
  • 135. Migrations number name 1 initial_schema 2 update_foo 3 implement_blah
  • 136. Migrations number name 1 initial_schema 2 update_foo 3 implement_blah
  • 137. Hand-rolled Database Migrator DEMO
  • 138. FMDB Migration Manager http://github.com/mocra/fmdb- migration-manager
  • 139. FMDB Migration Manager NSArray *migrations = [NSArray arrayWithObjects: [CreateStudents migration], // 1 [CreateStaffMembers migration], // 2 [AddStudentNumberToStudents migration], // 3 nil ]; [FmdbMigrationManager executeForDatabasePath:@"/tmp/tmp.db" withMigrations:migrations];
  • 140. FMDB Migration Manager @interface CreateStudents : FmdbMigration { } @end @implementation CreateStudents - (void)up { [self createTable:@"students" withColumns:[NSArray arrayWithObjects: [FmdbMigrationColumn columnWithColumnName:@"first_name" columnType:@"string"], [FmdbMigrationColumn columnWithColumnName:@"age" columnType:@"integer" defaultValue:21], nil]; } - (void)down { [self dropTable:@"students"]; } @end
  • 141. ActiveRecord Person *p = [[Person alloc] init]; p.name = @"Joe"; p.occupation = @"Dishwasher"; [p save]; Person *p = [Person personWithId:5]; ==> Person {id:5, name:Joe, occupation:Dishwasher}