diff --git a/src/com/probertson/data/SQLRunner.as b/src/com/probertson/data/SQLRunner.as index 366c7f8..967b1c4 100644 --- a/src/com/probertson/data/SQLRunner.as +++ b/src/com/probertson/data/SQLRunner.as @@ -105,10 +105,13 @@ package com.probertson.data { var len:int = statementBatch.length; var statements:Vector. = new Vector.(len); + var parameters:Vector. = new Vector.(len); + if (_batchStmtCache == null) { _batchStmtCache = new Object(); } + for (var i:int = 0; i < len; i++) { var sql:String = statementBatch[i].statementText; @@ -120,20 +123,11 @@ package com.probertson.data _batchStmtCache[sql] = stmt; } - stmt.clearParameters(); - var params:Object = statementBatch[i].parameters; - if (params != null) - { - for (var prop:String in params) - { - stmt.parameters[":" + prop] = params[prop]; - } - } - statements[i] = stmt; + parameters[i] = statementBatch[i].parameters; } - var pendingBatch:PendingBatch = new PendingBatch(statements, resultHandler, errorHandler, progressHandler); + var pendingBatch:PendingBatch = new PendingBatch(statements, parameters, resultHandler, errorHandler, progressHandler); _connection.addBlockingBatch(pendingBatch); } diff --git a/src/com/probertson/data/sqlRunnerClasses/PendingBatch.as b/src/com/probertson/data/sqlRunnerClasses/PendingBatch.as index 3f19104..d76291d 100644 --- a/src/com/probertson/data/sqlRunnerClasses/PendingBatch.as +++ b/src/com/probertson/data/sqlRunnerClasses/PendingBatch.as @@ -30,14 +30,15 @@ package com.probertson.data.sqlRunnerClasses import flash.data.SQLTransactionLockType; import flash.errors.SQLError; import flash.events.SQLErrorEvent; - import flash.events.SQLEvent; + import flash.events.SQLEvent; public class PendingBatch { - public function PendingBatch(batch:Vector., resultHandler:Function, errorHandler:Function, progressHandler:Function=null) + public function PendingBatch(batch:Vector., parameters:Vector., resultHandler:Function, errorHandler:Function, progressHandler:Function=null) { _batch = batch; + _parameters = parameters; _resultHandler = resultHandler; _errorHandler = errorHandler; _progressHandler = progressHandler; @@ -47,6 +48,7 @@ package com.probertson.data.sqlRunnerClasses // ------- Member vars ------- private var _batch:Vector.; + private var _parameters:Vector.; private var _results:Vector.; private var _resultHandler:Function; private var _errorHandler:Function; @@ -94,28 +96,44 @@ package com.probertson.data.sqlRunnerClasses private function conn_begin(event:SQLEvent):void { _conn.removeEventListener(SQLEvent.BEGIN, conn_begin); + executeStatements(); } private function executeStatements():void + { + _results = new Vector.(); + executeNextStatement(); + } + + + private function executeNextStatement():void { callProgressHandler(); - - while (_batch.length > 0) + if (_batch.length > 0) { var stmt:SQLStatement = _batch.shift(); if (stmt.sqlConnection == null) { stmt.sqlConnection = _conn; } + + stmt.clearParameters(); + var params:Object = _parameters.shift(); + if (params != null) + { + for (var prop:String in params) + { + stmt.parameters[":" + prop] = params[prop]; + } + } + stmt.addEventListener(SQLEvent.RESULT, stmt_result); stmt.addEventListener(SQLErrorEvent.ERROR, conn_error); stmt.execute(); } - - _results = new Vector.(); } @@ -128,7 +146,7 @@ package com.probertson.data.sqlRunnerClasses _results[_results.length] = stmt.getResult(); _statementsCompleted++; - callProgressHandler(); + executeNextStatement(); if (_statementsCompleted == _numStatements) { @@ -245,6 +263,7 @@ package com.probertson.data.sqlRunnerClasses _conn = null; _pool = null; _batch = null; + _parameters = null; _results = null; _progressHandler = null; _resultHandler = null; diff --git a/tests/events/ExecuteModifyErrorEvent.as b/tests/events/ExecuteModifyErrorEvent.as new file mode 100644 index 0000000..5acdd40 --- /dev/null +++ b/tests/events/ExecuteModifyErrorEvent.as @@ -0,0 +1,40 @@ +package events +{ + import flash.errors.SQLError; + import flash.events.Event; + + public class ExecuteModifyErrorEvent extends Event + { + // ------- Event type constants ------- + + public static const ERROR:String = "executeModifyError"; + + + // ------- Constructor ------- + + public function ExecuteModifyErrorEvent(type:String, error:SQLError, bubbles:Boolean=false, cancelable:Boolean=false) + { + super(type, bubbles, cancelable); + this.error = error; + } + + + // ------- Public properties ------- + + public var error:SQLError; + + + // ------- Event overrides ------- + + public override function clone():Event + { + return new ExecuteModifyErrorEvent(type, error, bubbles, cancelable); + } + + + public override function toString():String + { + return formatToString("ExecuteModifyErrorEvent", "type", "bubbles", "cancelable", "eventPhase"); + } + } +} \ No newline at end of file diff --git a/tests/events/ExecuteModifyResultEvent.as b/tests/events/ExecuteModifyResultEvent.as new file mode 100644 index 0000000..65dbce7 --- /dev/null +++ b/tests/events/ExecuteModifyResultEvent.as @@ -0,0 +1,40 @@ +package events +{ + import flash.data.SQLResult; + import flash.events.Event; + + public class ExecuteModifyResultEvent extends Event + { + // ------- Event type constants ------- + + public static const RESULT:String = "executeModifyResult"; + + + // ------- Constructor ------- + + public function ExecuteModifyResultEvent(type:String, results:Vector., bubbles:Boolean=false, cancelable:Boolean=false) + { + super(type, bubbles, cancelable); + this.results = results; + } + + + // ------- Public properties ------- + + public var results:Vector.; + + + // ------- Event overrides ------- + + public override function clone():Event + { + return new ExecuteModifyResultEvent(type, results, bubbles, cancelable); + } + + + public override function toString():String + { + return formatToString("ExecuteModifyResultEvent", "type", "bubbles", "cancelable", "eventPhase"); + } + } +} \ No newline at end of file diff --git a/tests/sql/AddRow.sql b/tests/sql/AddRow.sql new file mode 100644 index 0000000..c828f14 --- /dev/null +++ b/tests/sql/AddRow.sql @@ -0,0 +1,10 @@ +INSERT INTO main.testTable +( + colString, + colInt +) +VALUES +( + :colString, + :colInt +) \ No newline at end of file diff --git a/tests/sql/create/CreateTable_testTable.sql b/tests/sql/create/CreateTable_testTable.sql new file mode 100644 index 0000000..b646012 --- /dev/null +++ b/tests/sql/create/CreateTable_testTable.sql @@ -0,0 +1,6 @@ +CREATE TABLE main.testTable +( + colIntPK int PRIMARY KEY AUTOINCREMENT, + colString String NOT NULL, + colInt int +) \ No newline at end of file diff --git a/tests/tests/com/probertson/data/SQLRunnerExecuteModifyTest.as b/tests/tests/com/probertson/data/SQLRunnerExecuteModifyTest.as new file mode 100644 index 0000000..aaf847f --- /dev/null +++ b/tests/tests/com/probertson/data/SQLRunnerExecuteModifyTest.as @@ -0,0 +1,154 @@ +package tests.com.probertson.data +{ + import com.probertson.data.QueuedStatement; + import com.probertson.data.SQLRunner; + import events.ExecuteModifyErrorEvent; + import events.ExecuteModifyResultEvent; + import flash.data.SQLConnection; + import flash.data.SQLResult; + import flash.data.SQLStatement; + import flash.errors.SQLError; + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.filesystem.File; + import flexunit.framework.Assert; + import org.flexunit.async.Async; + import utils.CreateDatabase; + + public class SQLRunnerExecuteModifyTest extends EventDispatcher + { + // Reference declaration for class to test + private var _sqlRunner:com.probertson.data.SQLRunner; + + + public function SQLRunnerExecuteModifyTest() + { + } + + + // ------- Instance vars ------- + + private var _dbFile:File; + + + // ------- Setup/cleanup ------- + + [Before] + public function setup():void + { + _dbFile = File.createTempDirectory().resolvePath("test.db"); + var createDB:CreateDatabase = new CreateDatabase(_dbFile); + createDB.createDatabase(); + } + + + [After(async, timeout="250")] + public function teardown():void + { + _sqlRunner.close(sqlRunner_close); + } + + private function sqlRunner_close():void + { + _sqlRunner = null; + var tempDir:File = _dbFile.parent; + tempDir.deleteDirectory(true); + } + + + // ------- Tests ------- + + [Test(async, timeout="500")] + public function testOneStatement():void + { + addEventListener(ExecuteModifyResultEvent.RESULT, Async.asyncHandler(this, testOneStatement_result2, 500)); + + _sqlRunner = new SQLRunner(_dbFile); + var stmt:QueuedStatement = new QueuedStatement(ADD_ROW_SQL, {colString:"Hello", colInt:7}); + _sqlRunner.executeModify(Vector.([stmt]), testOneStatement_result, testOneStatement_error); + } + + // --- handlers --- + + private function testOneStatement_result(results:Vector.):void + { + dispatchEvent(new ExecuteModifyResultEvent(ExecuteModifyResultEvent.RESULT, results)); + } + + private function testOneStatement_result2(event:ExecuteModifyResultEvent, passThroughData:Object):void + { + Assert.assertEquals(1, event.results.length); + Assert.assertEquals(1, event.results[0].rowsAffected); + } + + private function testOneStatement_error(error:SQLError):void + { + Assert.fail(error.message); + } + + + [Test(async, timeout="500")] + public function testReuseStatement():void + { + addEventListener(ExecuteModifyResultEvent.RESULT, Async.asyncHandler(this, testReuseStatement_result2, 500)); + + _sqlRunner = new SQLRunner(_dbFile); + var stmt1:QueuedStatement = new QueuedStatement(ADD_ROW_SQL, {colString:"Hello", colInt:7}); + var stmt2:QueuedStatement = new QueuedStatement(ADD_ROW_SQL, {colString:"World", colInt:17}); + _sqlRunner.executeModify(Vector.([stmt1, stmt2]), testReuseStatement_result, testReuseStatement_error); + } + + // --- handlers --- + + private function testReuseStatement_result(results:Vector.):void + { + dispatchEvent(new ExecuteModifyResultEvent(ExecuteModifyResultEvent.RESULT, results)); + } + + private function testReuseStatement_result2(event:ExecuteModifyResultEvent, passThroughData:Object):void + { + // verify that the inserts happened + Assert.assertEquals(2, event.results.length); + Assert.assertEquals(1, event.results[0].rowsAffected); + Assert.assertEquals(1, event.results[1].rowsAffected); + + // verify that the inserted data matches + var id1:int = event.results[0].lastInsertRowID; + var id2:int = event.results[1].lastInsertRowID; + + var conn:SQLConnection = new SQLConnection(); + conn.open(_dbFile); + + var stmt:SQLStatement = new SQLStatement(); + stmt.sqlConnection = conn; + stmt.text = "SELECT colString, colInt FROM main.testTable WHERE colIntPK = :colIntPK"; + var result:SQLResult; + + stmt.parameters[":colIntPK"] = id1; + stmt.execute(); + result = stmt.getResult(); + Assert.assertEquals("Hello", result.data[0].colString); + Assert.assertEquals(7, result.data[0].colInt); + + stmt.parameters[":colIntPK"] = id2; + stmt.execute(); + result = stmt.getResult(); + Assert.assertEquals("World", result.data[0].colString); + Assert.assertEquals(17, result.data[0].colInt); + + conn.close(); + } + + private function testReuseStatement_error(error:SQLError):void + { + Assert.fail(error.message); + } + + + // ------- SQL statements ------- + + [Embed(source="sql/AddRow.sql", mimeType="application/octet-stream")] + private static const AddRowStatementText:Class; + private static const ADD_ROW_SQL:String = new AddRowStatementText(); + } +} \ No newline at end of file diff --git a/tests/utils/CreateDatabase.as b/tests/utils/CreateDatabase.as new file mode 100644 index 0000000..5862def --- /dev/null +++ b/tests/utils/CreateDatabase.as @@ -0,0 +1,43 @@ +package utils +{ + import flash.data.SQLConnection; + import flash.data.SQLStatement; + import flash.filesystem.File; + + public class CreateDatabase + { + + public function CreateDatabase(dbFile:File=null) + { + this.dbFile = dbFile; + } + + + // ------- Public properties ------- + + public var dbFile:File; + + + // ------- Public methods ------- + + public function createDatabase():void + { + var conn:SQLConnection = new SQLConnection(); + conn.open(dbFile); + + var stmt:SQLStatement = new SQLStatement(); + stmt.sqlConnection = conn; + stmt.text = CREATE_TABLE_SQL; + stmt.execute(); + + conn.close(); + } + + + // ------- SQL statements ------- + + [Embed(source="sql/create/CreateTable_testTable.sql", mimeType="application/octet-stream")] + private static const CreateTableStatementText:Class; + private static const CREATE_TABLE_SQL:String = new CreateTableStatementText(); + } +} \ No newline at end of file