Skip to content

Commit

Permalink
fixed some migration code
Browse files Browse the repository at this point in the history
  • Loading branch information
mrjjwright committed Apr 5, 2010
1 parent 5607727 commit a18f87f
Show file tree
Hide file tree
Showing 9 changed files with 454 additions and 275 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Options object
You can pass as options object to nosqlite like so: You can pass as options object to nosqlite like so:


options: {no_guid: true} options: {no_guid: true}
db: nosqlite.connect(sqlite.openDatabaseSync(db_file), options) db: nosqlite.connect(db_file, options, callback)


Here are the options supported Here are the options supported


Expand Down Expand Up @@ -126,5 +126,6 @@ Currently Requires


* [node](http://nodejs.org) * [node](http://nodejs.org)
* [CoffeeScript](http://jashkenas.github.com/coffee-script/) - fun, clean way to write JavaScript. Includes Cake to run the Cakefile and tests. * [CoffeeScript](http://jashkenas.github.com/coffee-script/) - fun, clean way to write JavaScript. Includes Cake to run the Cakefile and tests.
* [node-sqlite](http://github.com/orlandov/node-sqlite) You will have to get it and compile the node bindings and put it in your node requires path. * [node-sqlite](http://github.com/mrjjwright/nosqlite) My fork of it. You will have to get it and compile the node bindings and put it in your node requires path.
* [restler](http://github.com/danwrong/restler) - only needed to execute the tests for web API. Not needed otherwise. * [restler](http://github.com/danwrong/restler) - only needed to execute the tests for web API. Not needed otherwise.
* (NoSQLite ships with these libraries but you don't need to install everything: [flow.js](http://github.com/willconant/flow-js), [underscore.js](http://documentcloud.github.com/underscore/), [Math.uuid.js](http://www.broofa.com/2008/09/javascript-uuid-function/)
114 changes: 61 additions & 53 deletions nosqlite.coffee
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ sqlite: require "./sqlite"
# --------------------------------- # ---------------------------------
# #
# A library to make it as easy as possible to store and retrieve JS objects # A library to make it as easy as possible to store and retrieve JS objects
# from SQLite. Zero-configuration! # from SQLite. Zero-configuration!
# Attempts to store JS objects as intelligently as possible in SQLite. # Attempts to store JS objects as intelligently as possible in SQLite.
class NoSQLite class NoSQLite


Expand All @@ -28,7 +28,7 @@ class NoSQLite
no_guid: false no_guid: false
} }


if _.isFunction(options) if _.isFunction(options)
the_callback: options the_callback: options
else else
@options: _.extend(@options, options) if options? @options: _.extend(@options, options) if options?
Expand All @@ -46,29 +46,20 @@ class NoSQLite
# The following is the supported predicate syntax: # The following is the supported predicate syntax:
# #
# As always, we will call you back when everything is ready! # As always, we will call you back when everything is ready!
find: (table, predicate, callback) -> find: (table, predicate, the_callback) ->
select: sql.select(table, predicate) select: sql.select(table, predicate)
db: @db db: @db
self: this self: this
table: table callback: the_callback
predicate: predicate callback: predicate if _.isFunction(predicate)
@hash_flag: true sys.p select.escaped
db.query select.escaped, (error, results) ->
if error? then return callback(error)
callback(null, results)


try
db.query(select.escaped, (res) ->
callback(null, res)
)
catch the_err
debug "error on find: " + the_err
err: if the_err.message? then the_err.message else the_err
self.parse_error(err)
switch self.errobj.code
when self.NO_SUCH_TABLE then callback("NoSQLite doesn't know about this table yet. Either call save or create_table.")
when self.NO_SUCH_COLUMN then callback("NoSQLite can create this column for you if you call create_table with an object with that property")
else callback(err)


# Find the object in the database identified by the predicate # Find the object in the database identified by the predicate
# if it exists. Otherwise, saves it. # if it exists. Otherwise, saves it.
# Use this method if you need to save stuff in SQLite if it's not already there. # Use this method if you need to save stuff in SQLite if it's not already there.
# This is useful for times you aren't sure if the object is already in the db. # This is useful for times you aren't sure if the object is already in the db.
# and you don't have the rowid on the obj (othewise you could just do a save, which # and you don't have the rowid on the obj (othewise you could just do a save, which
Expand Down Expand Up @@ -150,7 +141,7 @@ class NoSQLite
# * Dates are stored as numbers, Unix epochs since 1970 # * Dates are stored as numbers, Unix epochs since 1970
# * Booleans are stored as numbers, 1 for true or 0 for false # * Booleans are stored as numbers, 1 for true or 0 for false
# * Other objects (arrays, complex objects) are simply stored as JSON.stringify text # * Other objects (arrays, complex objects) are simply stored as JSON.stringify text
# You can pass in an array of objects as well. Each row will be inserted # You can pass in an array of objects as well. Each row will be inserted
# #
# As always, we'll call you back when everything is ready! # As always, we'll call you back when everything is ready!
save: (table, obj, in_transaction, the_callback) -> save: (table, obj, in_transaction, the_callback) ->
Expand All @@ -167,8 +158,7 @@ class NoSQLite
if _.isBoolean(in_transaction) if _.isBoolean(in_transaction)
tx_flag: in_transaction tx_flag: in_transaction
callback: the_callback callback: the_callback



inserts: [] inserts: []
inserts: sql.insert(table, table_obj, @options.core_data_mode) for table_obj in obj if _.isArray(obj) inserts: sql.insert(table, table_obj, @options.core_data_mode) for table_obj in obj if _.isArray(obj)
inserts.push(sql.insert(table, obj, @options.core_data_mode)) if not _.isArray(obj) inserts.push(sql.insert(table, obj, @options.core_data_mode)) if not _.isArray(obj)
Expand All @@ -187,7 +177,7 @@ class NoSQLite
# save the first one # save the first one
self_this: this self_this: this
try_first_one: -> try_first_one: ->
db.query inserts[0].escaped, null, (err) -> db.query inserts[0].escaped, null, (err, result) ->
if err? if err?
# This is NoSQLite, let's see if we can fix this! # This is NoSQLite, let's see if we can fix this!
compensating_sql: self.compensating_sql(table, the_obj, err) compensating_sql: self.compensating_sql(table, the_obj, err)
Expand All @@ -204,10 +194,8 @@ class NoSQLite
# save the rest # save the rest
self_this: this self_this: this
do_insert: (i) -> do_insert: (i) ->
db.query inserts[i].escaped, (err) -> db.query inserts[i].escaped, (err, result) ->
if err? if err? then return callback(err)
callback(err)
return
if i-- then do_insert(i) if i-- then do_insert(i)
else self_this() else self_this()
if inserts.length > 1 then do_insert(inserts.length-1) if inserts.length > 1 then do_insert(inserts.length-1)
Expand All @@ -233,8 +221,7 @@ class NoSQLite
else null else null




# closes any underlying SQLite connection # closes the underlying SQLite connection
# currently, this means closes the underlying SQLite db process
close: -> close: ->
@db.close(-> @db.close(->
) )
Expand All @@ -260,7 +247,7 @@ class NoSQLite


# Migrations # Migrations
# ------------------------------------- # -------------------------------------
# A handy utility for doing a SQLite table schema migration. # A handy utility for doing a SQLite table data or schema migration.
# #
# If something goes wrong here at the wrong time, # If something goes wrong here at the wrong time,
# not that it will, I know you have a backup. :) # not that it will, I know you have a backup. :)
Expand All @@ -273,7 +260,7 @@ class NoSQLite
# should implicitly describe (using nosqlite conventions, detailed in docs for save) # should implicitly describe (using nosqlite conventions, detailed in docs for save)
# the new schema that will be used to create the new table. # the new schema that will be used to create the new table.
# The first row will be inserted and convert_callback will be called for # The first row will be inserted and convert_callback will be called for
# for every other row in the temp table. # for every other row in the temp table. You can do data conversions in this callback
# #
# Finally, the temp table is deleted and the callback(err, res) function is called. # Finally, the temp table is deleted and the callback(err, res) function is called.
# If any errors occur, callback(err) will be called. # If any errors occur, callback(err) will be called.
Expand All @@ -282,42 +269,63 @@ class NoSQLite
migrate_table: (table, convert_callback, callback) -> migrate_table: (table, convert_callback, callback) ->
self: this self: this
row1: {} row1: {}
temp_table_name: "${table}_backup"
sys.debug "Migrating table: ${table}"
flow.exec( flow.exec(
-> ->
#create the temp table self.db.query "begin transaction", this
->
# create the temp table
this_flow: this
self.find table, {rowid: 1}, (err, res) -> self.find table, {rowid: 1}, (err, res) ->
row1: res[0] row1: res[0]
create_temp_table(row1, this) create_temp_table_sql: sql.create_temp_table(table, row1)
self.db.query create_temp_table_sql, this_flow
->
# dump all rows to the temp table
this_flow: this
select_sql: sql.select(table).escaped
dump_sql: "insert into ${temp_table_name} ${select_sql};"
self.db.query dump_sql, (err, res) ->
if err? then return callback(err)
this_flow()
-> ->
#drop and recreate the table
this_flow: this this_flow: this
#convert and save the first row create_table_sql: sql.create_table(table, row1).sql
drop_table_sql: "drop table ${table}"
self.db.query drop_table_sql, (err, res) ->
if err? then return callback(err)
self.db.query create_table_sql, (err, res) ->
if err? then return callback(err)
this_flow()
->
this_flow: this
# convert and save the first row to new table
new_obj: convert_callback(row1) new_obj: convert_callback(row1)
self.save table, new_obj, (err, res) -> in_transaction: true
this_flow()
self.save table, new_obj, in_transaction, (err, res) ->
if err? then callback(err) if err? then callback(err)
this_flow() this_flow()
-> ->
this_flow() this_flow: this
#convert the rest of the rows # convert the rest of the rows and save to new table
self.find table, {'rowid >': 1}, (err, res) -> self.find temp_table_name, {"rowid >": 0}, (err, res) ->
if err? then return callback(err)
if res.length <= 1 then this_flow()
for row in res for row in res
converted_obj: convert_callback(row) converted_obj: convert_callback(row)
self.save table, converted_obj, (err, res) -> self.save table, converted_obj, (err, res) ->
if err? then callback(err) if err? then callback(err)
this_flow() this_flow()
-> ->
callback(null, "success") if callback? self.db.query "commit", (err, res) ->
if err? then return callback(err)
callback(null, "success") if callback?
) )
# create the temp table.
# We create it with the same number of cols as the old table
# We don't care about the types
create_temp_table: (obj, callback)->
# execute a pragma to get the number of cols in the old table
temp_cols: obj.keys.join(",")
temp_table_sql: "create temporary table ${table}_backup(${temp_cols});"
# this doesn't execute async (yet)
db.query(temp_table_sql)
callback()



# Web API # Web API
# -------------------------------------- # --------------------------------------
Expand All @@ -333,7 +341,7 @@ class NoSQLite
# Starts a webserver on the supplied port to serve http requests # Starts a webserver on the supplied port to serve http requests
# for the instance's associated database. # for the instance's associated database.
# If NoSQLite has already started a webserver on that port # If NoSQLite has already started a webserver on that port
# this method returns silently. # this method returns silently.
listen: (port, host) -> listen: (port, host) ->
host: "127.0.0.1" if not host? host: "127.0.0.1" if not host?
port: 5000 if not port? port: 5000 if not port?
Expand Down
Loading

0 comments on commit a18f87f

Please sign in to comment.