@@ -135,6 +135,11 @@ export default class SQLFeedback extends HParsonsFeedback {
135135 respDiv . parentElement . removeChild ( respDiv ) ;
136136 }
137137 $ ( this . output ) . text ( "" ) ;
138+ // creating new results div
139+ respDiv = document . createElement ( "div" ) ;
140+ respDiv . id = divid ;
141+ this . outDiv . appendChild ( respDiv ) ;
142+
138143 // Run this query
139144 let query = await this . buildProg ( ) ;
140145 if ( ! this . hparsons . db ) {
@@ -144,68 +149,46 @@ export default class SQLFeedback extends HParsonsFeedback {
144149 return ;
145150 }
146151
147- // TODO: cancel the execution if previous steps have errors
152+ let executionSuccessFlag = true ;
153+
154+ // executing hidden prefix if exist
148155 if ( query . prefix ) {
149156 this . prefixresults = this . executeIteratedStatements ( this . hparsons . db . iterateStatements ( query . prefix ) ) ;
157+ if ( this . prefixresults . at ( - 1 ) . status == 'failure' ) {
158+ // if error occured in hidden prefix, log and stop executing the rest
159+ this . visualizeResults ( respDiv , this . prefixresults , "Error executing hidden code in prefix" ) ;
160+ executionSuccessFlag = false ;
161+ }
150162 }
151- this . results = this . executeIteratedStatements ( this . hparsons . db . iterateStatements ( query . input ) ) ;
152- if ( query . suffix ) {
163+
164+ // executing student input in micro Parsons
165+ if ( executionSuccessFlag ) {
166+ this . results = this . executeIteratedStatements ( this . hparsons . db . iterateStatements ( query . input ) ) ;
167+ // always render full execution results of student input regardless of success/failure
168+ this . visualizeResults ( respDiv , this . results ) ;
169+ if ( this . results . at ( - 1 ) . status == 'failure' ) {
170+ // if error occured in student input, stop executing suffix/unitttest
171+ executionSuccessFlag = false ;
172+ }
173+ }
174+
175+ // executing hidden suffix if exist
176+ // In most cases the suffix is just "select * from x" to
177+ // see if the operations on the database is correct
178+ if ( executionSuccessFlag && query . suffix ) {
153179 this . suffixresults = this . executeIteratedStatements ( this . hparsons . db . iterateStatements ( query . suffix ) ) ;
180+ if ( this . suffixresults . at ( - 1 ) . status == 'failure' ) {
181+ // if error occured in hidden suffix, visualize the results
182+ this . visualizeResults ( respDiv , this . suffixresults , "Error executing hidden code in suffix" ) ;
183+ }
154184 }
155185
156- respDiv = document . createElement ( "div" ) ;
157- respDiv . id = divid ;
158- this . outDiv . appendChild ( respDiv ) ;
186+ // show the output div
159187 $ ( this . outDiv ) . show ( ) ;
160- // Sometimes we don't want to show a bunch of intermediate results
161- // like when we are including a bunch of previous statements from
162- // other activecodes In that case the showlastsql flag can be set
163- // so we only show the last result
164- let resultArray = this . results ;
165- for ( let r of resultArray ) {
166- let section = document . createElement ( "div" ) ;
167- section . setAttribute ( "class" , "hp_sql_result" ) ;
168- respDiv . appendChild ( section ) ;
169- if ( r . status === "success" ) {
170- if ( r . columns ) {
171- let tableDiv = document . createElement ( "div" ) ;
172- section . appendChild ( tableDiv ) ;
173- let maxHeight = 350 ;
174- if ( resultArray . length > 1 ) maxHeight = 200 ; // max height smaller if lots of results
175- createTable ( r , tableDiv , maxHeight ) ;
176- let messageBox = document . createElement ( "pre" ) ;
177- let rmsg = r . rowcount !== 1 ? " rows " : " row " ;
178- let msg = "" + r . rowcount + rmsg + "returned" ;
179- if ( r . rowcount > 100 ) {
180- msg = msg + " (only first 100 rows displayed)" ;
181- }
182- msg = msg + "." ;
183- messageBox . textContent = msg ;
184- messageBox . setAttribute ( "class" , "hp_sql_result_success" ) ;
185- section . appendChild ( messageBox ) ;
186- } else if ( r . rowcount ) {
187- let messageBox = document . createElement ( "pre" ) ;
188- let op = r . operation ;
189- op = op + ( op . charAt ( op . length - 1 ) === "e" ? "d." : "ed." ) ;
190- let rmsg = r . rowcount !== 1 ? " rows " : " row " ;
191- messageBox . textContent = "" + r . rowcount + rmsg + op ;
192- messageBox . setAttribute ( "class" , "hp_sql_result_success" ) ;
193- section . appendChild ( messageBox ) ;
194- } else {
195- let messageBox = document . createElement ( "pre" ) ;
196- messageBox . textContent = "Operation succeeded." ;
197- messageBox . setAttribute ( "class" , "hp_sql_result_success" ) ;
198- section . appendChild ( messageBox ) ;
199- }
200- } else {
201- let messageBox = document . createElement ( "pre" ) ;
202- messageBox . textContent = r . message ;
203- messageBox . setAttribute ( "class" , "hp_sql_result_failure" ) ;
204- section . appendChild ( messageBox ) ;
205- }
206- }
207188
208189 // Now handle autograding
190+ // autograding takes the results of the hidden suffix if exist
191+ // otherwise take the result of student input
209192 if ( this . hparsons . unittest ) {
210193 if ( this . suffixresults ) {
211194 this . testResult = this . autograde (
@@ -223,6 +206,26 @@ export default class SQLFeedback extends HParsonsFeedback {
223206 return Promise . resolve ( "done" ) ;
224207 }
225208
209+ // Refactored from activecode-sql.
210+ // Takes iterated statements from db.iterateStatemnts(queryString)
211+ // Returns Array<result>:
212+ /* each result: {
213+ status: "success" or "faliure",
214+ // for SELECT statements (?):
215+ columns: number of columns,
216+ values: data,
217+ rowcount: number of rows in data,
218+ // for INSERT, UPDATE, DELETE:
219+ operation: "INSERT", "UPDATE", or "DELETE",
220+ rowcount: number of rows modified,
221+ // when error occurred (aside from status):
222+ message: error message,
223+ sql: remaining SQL (?)
224+ // when no queries were executed:
225+ message: "no queries submitted"
226+ }*/
227+ // If an error occurs it will stop executing the rest of queries in it.
228+ // Thus the error result will always be the last item.
226229 executeIteratedStatements ( it ) {
227230 let results = [ ] ;
228231 try {
@@ -278,6 +281,66 @@ export default class SQLFeedback extends HParsonsFeedback {
278281 }
279282 return results ;
280283 }
284+
285+ // output the results in the resultArray(Array<results>).
286+ // container: the container that contains the results
287+ // resultArray (Array<result>): see executeIteratedStatements
288+ // Each result will be in a separate row.
289+ // devNote will be displayed in the top row if exist.
290+ // Current usage: "error executing hidden code in prefix/suffix"
291+ visualizeResults ( container , resultArray , devNote ) {
292+ if ( devNote ) {
293+ let section = document . createElement ( "div" ) ;
294+ section . setAttribute ( "class" , "hp_sql_result" ) ;
295+ container . appendChild ( section ) ;
296+ let messageBox = document . createElement ( "pre" ) ;
297+ messageBox . textContent = devNote ;
298+ messageBox . setAttribute ( "class" , "hp_sql_result_failure" ) ;
299+ section . appendChild ( messageBox ) ;
300+ }
301+ for ( let r of resultArray ) {
302+ let section = document . createElement ( "div" ) ;
303+ section . setAttribute ( "class" , "hp_sql_result" ) ;
304+ container . appendChild ( section ) ;
305+ if ( r . status === "success" ) {
306+ if ( r . columns ) {
307+ let tableDiv = document . createElement ( "div" ) ;
308+ section . appendChild ( tableDiv ) ;
309+ let maxHeight = 350 ;
310+ if ( resultArray . length > 1 ) maxHeight = 200 ; // max height smaller if lots of results
311+ createTable ( r , tableDiv , maxHeight ) ;
312+ let messageBox = document . createElement ( "pre" ) ;
313+ let rmsg = r . rowcount !== 1 ? " rows " : " row " ;
314+ let msg = "" + r . rowcount + rmsg + "returned" ;
315+ if ( r . rowcount > 100 ) {
316+ msg = msg + " (only first 100 rows displayed)" ;
317+ }
318+ msg = msg + "." ;
319+ messageBox . textContent = msg ;
320+ messageBox . setAttribute ( "class" , "hp_sql_result_success" ) ;
321+ section . appendChild ( messageBox ) ;
322+ } else if ( r . rowcount ) {
323+ let messageBox = document . createElement ( "pre" ) ;
324+ let op = r . operation ;
325+ op = op + ( op . charAt ( op . length - 1 ) === "e" ? "d." : "ed." ) ;
326+ let rmsg = r . rowcount !== 1 ? " rows " : " row " ;
327+ messageBox . textContent = "" + r . rowcount + rmsg + op ;
328+ messageBox . setAttribute ( "class" , "hp_sql_result_success" ) ;
329+ section . appendChild ( messageBox ) ;
330+ } else {
331+ let messageBox = document . createElement ( "pre" ) ;
332+ messageBox . textContent = "Operation succeeded." ;
333+ messageBox . setAttribute ( "class" , "hp_sql_result_success" ) ;
334+ section . appendChild ( messageBox ) ;
335+ }
336+ } else {
337+ let messageBox = document . createElement ( "pre" ) ;
338+ messageBox . textContent = r . message ;
339+ messageBox . setAttribute ( "class" , "hp_sql_result_failure" ) ;
340+ section . appendChild ( messageBox ) ;
341+ }
342+ }
343+ }
281344
282345 // adapted from activecode
283346 async buildProg ( ) {
0 commit comments