 		return map
 	}
 
-	/// Execute the statement without returning a result set
+	/// Returns the name of the column at `index`.
+	///
+	/// - note: Column indexes are 0-based.  The leftmost column in a result row has index 0.
+	/// - requires: `index >= 0`
+	/// - requires: `index < self.columnCount`
+	///
+	/// - parameter index: The index of the desired column
+	///
+	/// - returns: The name of the column for the specified index
+	/// - throws: An error if `index` is out of bounds
+	public func name(ofColumn index: Int) throws -> String {
+		guard index >= 0, index < self.columnCount else {
+			throw DatabaseError.sqliteError("Column index \(index) out of bounds")
+		}
+		return String(cString: sqlite3_column_name(stmt, Int32(index)))
+	}
+
+	/// Executes the statement and applies `block` to each result row.
+	///
+	/// - parameter block: A closure applied to each result row
+	/// - parameter row: A result row of returned data
 	///
 	/// - throws: `DatabaseError`
-	public func execute() throws {
+	public func execute(_ block: ((_ row: Row) throws -> ())? = nil) throws {
 		var result = sqlite3_step(stmt)
 		while result == SQLITE_ROW {
+			try block?(Row(statement: self))
 			result = sqlite3_step(stmt)
 		}
 
 		if result != SQLITE_DONE {
-			#if DEBUG
-				print("Error executing statement: \(String(cString: sqlite3_errmsg(sqlite3_db_handle(stmt))))")
-			#endif
-			throw DatabaseError.sqliteError(String(cString: sqlite3_errmsg(sqlite3_db_handle(stmt))))
-		}
-	}
-
-	/// Iterate through the rows in the result set
-	///
-	/// - parameter block: A block called for each row
-	/// - parameter row: A `Row` object representing a row of returned data
-	/// - throws: `DatabaseError`
-	public func results(row block: (_ row: Row) throws -> ()) throws {
-		var result = sqlite3_step(stmt)
-		while result == SQLITE_ROW {
-			try block(Row(statement: self))
-			result = sqlite3_step(stmt)
-		}
-
-		if result != SQLITE_DONE {
-			#if DEBUG
-				print("Error executing statement: \(String(cString: sqlite3_errmsg(sqlite3_db_handle(stmt))))")
-			#endif
-			throw DatabaseError.sqliteError(String(cString: sqlite3_errmsg(sqlite3_db_handle(stmt))))
-		}
-	}
-
-	/// Reset the statement to its initial state
-	///
-	/// - throws: `DatabaseError`
+			throw DatabaseError.sqliteError("Error executing statement: \(String(cString: sqlite3_errmsg(sqlite3_db_handle(stmt))))")
+		}
+	}
+
+	/// Resets the statement to its initial state, ready to be re-executed.
+	///
+	/// - note: This function does not change the value of  any bound SQL parameters.
+	///
+	/// - throws: An error if the statement could not be reset
 	public func reset() throws {
 		if sqlite3_reset(stmt) != SQLITE_OK {
-			#if DEBUG
-				print("Error resetting statement: \(String(cString: sqlite3_errmsg(sqlite3_db_handle(stmt))))")
-			#endif
-			throw DatabaseError.sqliteError(String(cString: sqlite3_errmsg(sqlite3_db_handle(stmt))))
-		}
-	}
-
-	/// Clear all statement bindings
-	///
-	/// - throws: `DatabaseError`
+			throw DatabaseError.sqliteError("Error resetting statement: \(String(cString: sqlite3_errmsg(sqlite3_db_handle(stmt))))")
+		}
+	}
+
+	/// Clears all statement bindings by setting SQL parameters to null.
+	///
+	/// - throws: An error if the bindings could not be cleared
 	public func clearBindings() throws {
 		if sqlite3_clear_bindings(stmt) != SQLITE_OK {
-			#if DEBUG
-				print("Error clearing bindings: \(String(cString: sqlite3_errmsg(sqlite3_db_handle(stmt))))")
-			#endif
-			throw DatabaseError.sqliteError(String(cString: sqlite3_errmsg(sqlite3_db_handle(stmt))))
-		}
-	}
-
-	/// Perform a low-level statement operation
+			throw DatabaseError.sqliteError("Error clearing bindings: \(String(cString: sqlite3_errmsg(sqlite3_db_handle(stmt))))")
+		}
+	}
+
+	/// Performs a low-level SQLite statement operation.
 	///
 	/// **Use of this function should be avoided whenever possible**
 	///
-	/// - parameter block: The block performing the operation
+	/// - parameter block: A closure performing the operation
 	/// - parameter stmt: The raw `sqlite3_stmt *` statement object
+	///
 	/// - throws: Any error thrown in `block`
+	///
 	/// - returns: The value returned by `block`
 	public func withUnsafeRawSQLiteStatement<T>(block: (_ stmt: SQLitePreparedStatement) throws -> (T)) rethrows -> T {
 		return try block(stmt)
 	}
 }
 
-extension Statement {
-	/// Get a sequence for accessing the rows in the result set
-	///
-	/// Because the underlying iterator discards errors, the preferred way for accessing rows is
-	/// via the block-based `results(row:)` function
-	///
-	/// - returns: A sequence for accessing the rows in the result set
-	public func results() -> AnySequence<Row> {
-		return AnySequence {
-			return AnyIterator {
-				let stmt = self.stmt
-				switch sqlite3_step(stmt) {
-				case SQLITE_ROW:
-					return Row(statement: self)
-				case SQLITE_DONE:
-					return nil
-				default:
-					#if DEBUG
-						print("Error executing statement: \(String(cString: sqlite3_errmsg(sqlite3_db_handle(stmt))))")
-					#endif
-					return nil
-				}
+extension Statement: Sequence {
+	/// Returns an iterator for accessing the result rows.
+	///
+	/// Because the iterator discards errors, the preferred way of accessing result rows
+	/// is via the block-based `execute(_:)` function
+	///
+	/// - returns: An iterator over the result rows
+	public func makeIterator() -> AnyIterator<Row> {
+		return AnyIterator {
+			let stmt = self.stmt
+			switch sqlite3_step(stmt) {
+			case SQLITE_ROW:
+				return Row(statement: self)
+			case SQLITE_DONE:
+				return nil
+			default:
+				#if DEBUG
+					print("Error executing statement: \(String(cString: sqlite3_errmsg(sqlite3_db_handle(stmt))))")
+				#endif
+				return nil
 			}
 		}
 	}


import Foundation

/// An `sqlite3_stmt *` object
///
/// - seealso: [SQLite Prepared Statement Object](https://sqlite.org/c3ref/stmt.html)
public typealias SQLitePreparedStatement = OpaquePointer

/// A class representing an SQL statement with support for binding SQL parameters and retrieving results.
final public class Statement {
	/// The owning `Database`
	public let database: Database

	/// The underlying `sqlite3_stmt *` object
	var stmt: SQLitePreparedStatement

	/// Compile an SQL statement
	///
	/// - parameter database: The owning database
	/// - parameter sql: The SQL statement to compile
	/// - throws: `DatabaseError`
	init(database: Database, sql: String) throws {
		self.database = database

		var stmt: SQLitePreparedStatement? = nil
		guard sqlite3_prepare_v2(database.db, sql, -1, &stmt, nil) == SQLITE_OK else {
			#if DEBUG
				print("Error preparing SQL \"\(sql)\"")
				print("Error message: \(String(cString: sqlite3_errmsg(database.db)))")
			#endif
			throw DatabaseError.sqliteError(String(cString: sqlite3_errmsg(database.db)))
		}

		self.stmt = stmt!

	/// The mapping of column names to indexes
	var columnNamesAndIndexes: [String: Int] {
		let columnCount = sqlite3_column_count(stmt)
		var map = [String: Int](minimumCapacity: Int(columnCount))
		for i in 0..<columnCount {
			let name = String(cString: sqlite3_column_name(stmt, i))
			map[name] = Int(i)
