зеркало из https://github.com/mozilla/gecko-dev.git
Bug 395144 - Figure out what to do when we are supposed to open an application with download resume. r=sdwilsh, b-ff3=beltzner, aM9=beltzner
This commit is contained in:
Родитель
607af1ac22
Коммит
6b0e3ba0e9
|
@ -66,6 +66,7 @@ REQUIRES = xpcom \
|
|||
alerts \
|
||||
storage \
|
||||
xulapp \
|
||||
exthandler \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
|
|
|
@ -74,6 +74,9 @@
|
|||
#include "nsIDownloadManagerUI.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIResumableChannel.h"
|
||||
#include "nsCExternalHandlerService.h"
|
||||
#include "nsIExternalHelperAppService.h"
|
||||
#include "nsIMIMEService.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include <shlobj.h>
|
||||
|
@ -89,10 +92,11 @@ static PRBool gStoppingDownloads = PR_FALSE;
|
|||
#define PREF_BDM_RETENTION "browser.download.manager.retention"
|
||||
#define PREF_BDM_CLOSEWHENDONE "browser.download.manager.closeWhenDone"
|
||||
#define PREF_BDM_ADDTORECENTDOCS "browser.download.manager.addToRecentDocs"
|
||||
#define PREF_BH_DELETETEMPFILEONEXIT "browser.helperApps.deleteTempFileOnExit"
|
||||
|
||||
static const PRInt64 gUpdateInterval = 400 * PR_USEC_PER_MSEC;
|
||||
|
||||
#define DM_SCHEMA_VERSION 6
|
||||
#define DM_SCHEMA_VERSION 7
|
||||
#define DM_DB_NAME NS_LITERAL_STRING("downloads.sqlite")
|
||||
#define DM_DB_CORRUPT_FILENAME NS_LITERAL_STRING("downloads.sqlite.corrupt")
|
||||
|
||||
|
@ -323,6 +327,30 @@ nsDownloadManager::InitDB(PRBool *aDoImport)
|
|||
}
|
||||
// Fallthrough to the next upgrade
|
||||
|
||||
case 6: // This version adds three columns to DB (MIME type related info)
|
||||
{
|
||||
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"ALTER TABLE moz_downloads "
|
||||
"ADD COLUMN mimeType TEXT"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"ALTER TABLE moz_downloads "
|
||||
"ADD COLUMN preferredApplication TEXT"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"ALTER TABLE moz_downloads "
|
||||
"ADD COLUMN preferredAction INTEGER NOT NULL DEFAULT 0"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Finally, update the schemaVersion variable and the database schema
|
||||
schemaVersion = 7;
|
||||
rv = mDBConn->SetSchemaVersion(schemaVersion);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
// Fallthrough to next upgrade
|
||||
|
||||
// Extra sanity checking for developers
|
||||
#ifndef DEBUG
|
||||
case DM_SCHEMA_VERSION:
|
||||
|
@ -353,7 +381,8 @@ nsDownloadManager::InitDB(PRBool *aDoImport)
|
|||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT id, name, source, target, tempPath, startTime, endTime, state, "
|
||||
"referrer, entityID, currBytes, maxBytes "
|
||||
"referrer, entityID, currBytes, maxBytes, mimeType, "
|
||||
"preferredApplication, preferredAction "
|
||||
"FROM moz_downloads"), getter_AddRefs(stmt));
|
||||
if (NS_SUCCEEDED(rv))
|
||||
break;
|
||||
|
@ -398,7 +427,10 @@ nsDownloadManager::CreateTable()
|
|||
"referrer TEXT, "
|
||||
"entityID TEXT, "
|
||||
"currBytes INTEGER NOT NULL DEFAULT 0, "
|
||||
"maxBytes INTEGER NOT NULL DEFAULT -1"
|
||||
"maxBytes INTEGER NOT NULL DEFAULT -1, "
|
||||
"mimeType TEXT, "
|
||||
"preferredApplication TEXT, "
|
||||
"preferredAction INTEGER NOT NULL DEFAULT 0"
|
||||
")"));
|
||||
}
|
||||
|
||||
|
@ -535,7 +567,8 @@ nsDownloadManager::ImportDownloadHistory()
|
|||
if (NS_FAILED(rv)) continue;
|
||||
|
||||
(void)AddDownloadToDB(name, source, target, EmptyString(), startTime,
|
||||
endTime, state);
|
||||
endTime, state, EmptyCString(), EmptyCString(),
|
||||
nsIMIMEInfo::saveToDisk);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -636,13 +669,17 @@ nsDownloadManager::AddDownloadToDB(const nsAString &aName,
|
|||
const nsAString &aTempPath,
|
||||
PRInt64 aStartTime,
|
||||
PRInt64 aEndTime,
|
||||
PRInt32 aState)
|
||||
PRInt32 aState,
|
||||
const nsACString &aMimeType,
|
||||
const nsACString &aPreferredApp,
|
||||
nsHandlerInfoAction aPreferredAction)
|
||||
{
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"INSERT INTO moz_downloads "
|
||||
"(name, source, target, tempPath, startTime, endTime, state) "
|
||||
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"), getter_AddRefs(stmt));
|
||||
"(name, source, target, tempPath, startTime, endTime, state, "
|
||||
"mimeType, preferredApplication, preferredAction) "
|
||||
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)"), getter_AddRefs(stmt));
|
||||
NS_ENSURE_SUCCESS(rv, 0);
|
||||
|
||||
PRInt32 i = 0;
|
||||
|
@ -674,6 +711,18 @@ nsDownloadManager::AddDownloadToDB(const nsAString &aName,
|
|||
rv = stmt->BindInt32Parameter(i++, aState);
|
||||
NS_ENSURE_SUCCESS(rv, 0);
|
||||
|
||||
// mimeType
|
||||
rv = stmt->BindUTF8StringParameter(i++, aMimeType);
|
||||
NS_ENSURE_SUCCESS(rv, 0);
|
||||
|
||||
// preferredApplication
|
||||
rv = stmt->BindUTF8StringParameter(i++, aPreferredApp);
|
||||
NS_ENSURE_SUCCESS(rv, 0);
|
||||
|
||||
// preferredAction
|
||||
rv = stmt->BindInt32Parameter(i++, aPreferredAction);
|
||||
NS_ENSURE_SUCCESS(rv, 0);
|
||||
|
||||
PRBool hasMore;
|
||||
rv = stmt->ExecuteStep(&hasMore); // we want to keep our lock
|
||||
NS_ENSURE_SUCCESS(rv, 0);
|
||||
|
@ -775,7 +824,8 @@ nsDownloadManager::GetDownloadFromDB(PRUint32 aID, nsDownload **retVal)
|
|||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT id, state, startTime, source, target, tempPath, name, referrer, "
|
||||
"entityID, currBytes, maxBytes "
|
||||
"entityID, currBytes, maxBytes, mimeType, preferredAction, "
|
||||
"preferredApplication "
|
||||
"FROM moz_downloads "
|
||||
"WHERE id = ?1"), getter_AddRefs(stmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -833,6 +883,52 @@ nsDownloadManager::GetDownloadFromDB(PRUint32 aID, nsDownload **retVal)
|
|||
PRInt64 maxBytes = stmt->AsInt64(i++);
|
||||
dl->SetProgressBytes(currBytes, maxBytes);
|
||||
|
||||
// Build mMIMEInfo only if the mimeType in DB is not empty
|
||||
nsCAutoString mimeType;
|
||||
rv = stmt->GetUTF8String(i++, mimeType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!mimeType.IsEmpty()) {
|
||||
nsCOMPtr<nsIMIMEService> mimeService =
|
||||
do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mimeService->GetFromTypeAndExtension(mimeType, EmptyCString(),
|
||||
getter_AddRefs(dl->mMIMEInfo));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsHandlerInfoAction action = stmt->AsInt32(i++);
|
||||
rv = dl->mMIMEInfo->SetPreferredAction(action);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCAutoString persistentDescriptor;
|
||||
rv = stmt->GetUTF8String(i++, persistentDescriptor);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!persistentDescriptor.IsEmpty()) {
|
||||
nsCOMPtr<nsILocalHandlerApp> handler =
|
||||
do_CreateInstance(NS_LOCALHANDLERAPP_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsILocalFile> localExecutable;
|
||||
rv = NS_NewNativeLocalFile(EmptyCString(), PR_FALSE,
|
||||
getter_AddRefs(localExecutable));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = localExecutable->SetPersistentDescriptor(persistentDescriptor);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = handler->SetExecutable(localExecutable);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = dl->mMIMEInfo->SetPreferredApplicationHandler(handler);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
} else {
|
||||
// Compensate for the i++s skipped in the true block
|
||||
i += 2;
|
||||
}
|
||||
|
||||
// Addrefing and returning
|
||||
NS_ADDREF(*retVal = dl);
|
||||
return NS_OK;
|
||||
|
@ -1090,9 +1186,33 @@ nsDownloadManager::AddDownload(DownloadType aDownloadType,
|
|||
if (aTempFile)
|
||||
aTempFile->GetPath(tempPath);
|
||||
|
||||
// Break down MIMEInfo but don't panic if we can't get all the pieces - we
|
||||
// can still download the file
|
||||
nsCAutoString persistentDescriptor, mimeType;
|
||||
nsHandlerInfoAction action = nsIMIMEInfo::saveToDisk;
|
||||
if (aMIMEInfo) {
|
||||
(void)aMIMEInfo->GetType(mimeType);
|
||||
|
||||
nsCOMPtr<nsIHandlerApp> handlerApp;
|
||||
(void)aMIMEInfo->GetPreferredApplicationHandler(getter_AddRefs(handlerApp));
|
||||
nsCOMPtr<nsILocalHandlerApp> locHandlerApp = do_QueryInterface(handlerApp);
|
||||
|
||||
if (locHandlerApp) {
|
||||
nsCOMPtr<nsIFile> executable;
|
||||
(void)locHandlerApp->GetExecutable(getter_AddRefs(executable));
|
||||
nsCOMPtr<nsILocalFile> locExecutable = do_QueryInterface(executable);
|
||||
|
||||
if (locExecutable)
|
||||
(void)locExecutable->GetPersistentDescriptor(persistentDescriptor);
|
||||
}
|
||||
|
||||
(void)aMIMEInfo->GetPreferredAction(&action);
|
||||
}
|
||||
|
||||
PRInt64 id = AddDownloadToDB(dl->mDisplayName, source, target, tempPath,
|
||||
dl->mStartTime, dl->mLastUpdate,
|
||||
nsIDownloadManager::DOWNLOAD_NOTSTARTED);
|
||||
nsIDownloadManager::DOWNLOAD_NOTSTARTED,
|
||||
mimeType, persistentDescriptor, action);
|
||||
NS_ENSURE_TRUE(id, NS_ERROR_FAILURE);
|
||||
dl->mID = id;
|
||||
|
||||
|
@ -2066,17 +2186,23 @@ nsDownload::ExecuteDesiredAction()
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsresult ret = NS_OK;
|
||||
nsresult retVal = NS_OK;
|
||||
switch (action) {
|
||||
case nsIMIMEInfo::saveToDisk:
|
||||
// Move the file to the proper location
|
||||
ret = MoveTempToTarget();
|
||||
retVal = MoveTempToTarget();
|
||||
break;
|
||||
case nsIMIMEInfo::useHelperApp:
|
||||
case nsIMIMEInfo::useSystemDefault:
|
||||
// For these cases we have to move the file to the target location and
|
||||
// open with the appropriate application
|
||||
retVal = OpenWithApplication();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -2107,6 +2233,60 @@ nsDownload::MoveTempToTarget()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDownload::OpenWithApplication()
|
||||
{
|
||||
// First move the temporary file to the target location
|
||||
nsCOMPtr<nsILocalFile> target;
|
||||
nsresult rv = GetTargetFile(getter_AddRefs(target));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Make sure the suggested name is unique since in this case we don't
|
||||
// have a file name that was guaranteed to be unique by going through
|
||||
// the File Save dialog
|
||||
rv = target->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Move the temporary file to the target location
|
||||
rv = MoveTempToTarget();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// We do not verify the return value here because, irrespective of success
|
||||
// or failure of the method, the deletion of temp file has to take place, as
|
||||
// per the corresponding preference. But we store this separately as this is
|
||||
// what we ultimately return from this function.
|
||||
nsresult retVal = mMIMEInfo->LaunchWithFile(target);
|
||||
|
||||
PRBool deleteTempFileOnExit;
|
||||
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
|
||||
if (!prefs || NS_FAILED(prefs->GetBoolPref(PREF_BH_DELETETEMPFILEONEXIT,
|
||||
&deleteTempFileOnExit))) {
|
||||
// No prefservice or no pref set; use default value
|
||||
#if !defined(XP_MACOSX)
|
||||
// Mac users have been very verbal about temp files being deleted on
|
||||
// app exit - they don't like it - but we'll continue to do this on
|
||||
// other platforms for now.
|
||||
deleteTempFileOnExit = PR_TRUE;
|
||||
#else
|
||||
deleteTempFileOnExit = PR_FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (deleteTempFileOnExit) {
|
||||
// Use the ExternalHelperAppService to push the temporary file to the list
|
||||
// of files to be deleted on exit.
|
||||
nsCOMPtr<nsPIExternalAppLauncher> appLauncher(do_GetService
|
||||
(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID));
|
||||
|
||||
// Even if we are unable to get this service we return the result
|
||||
// of LaunchWithFile() which makes more sense.
|
||||
if (appLauncher)
|
||||
(void)appLauncher->DeleteTemporaryFileOnExit(target);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void
|
||||
nsDownload::SetStartTime(PRInt64 aStartTime)
|
||||
{
|
||||
|
@ -2258,12 +2438,7 @@ nsDownload::IsPaused()
|
|||
PRBool
|
||||
nsDownload::IsResumable()
|
||||
{
|
||||
nsHandlerInfoAction action = nsIMIMEInfo::saveToDisk;
|
||||
if (mMIMEInfo)
|
||||
(void)mMIMEInfo->GetPreferredAction(&action);
|
||||
|
||||
// For now we can only resume saveToDisk type actions (not open with)
|
||||
return action == nsIMIMEInfo::saveToDisk && !mEntityID.IsEmpty();
|
||||
return !mEntityID.IsEmpty();
|
||||
}
|
||||
|
||||
PRBool
|
||||
|
|
|
@ -133,7 +133,10 @@ protected:
|
|||
const nsAString &aTempPath,
|
||||
PRInt64 aStartTime,
|
||||
PRInt64 aEndTime,
|
||||
PRInt32 aState);
|
||||
PRInt32 aState,
|
||||
const nsACString &aMimeType,
|
||||
const nsACString &aPreferredApp,
|
||||
nsHandlerInfoAction aPreferredAction);
|
||||
|
||||
void NotifyListenersOnDownloadStateChange(PRInt16 aOldState,
|
||||
nsIDownload *aDownload);
|
||||
|
@ -300,6 +303,18 @@ protected:
|
|||
*/
|
||||
nsresult FailDownload(nsresult aStatus, const PRUnichar *aMessage);
|
||||
|
||||
/**
|
||||
* Opens the downloaded file with the appropriate application, which is
|
||||
* either the OS default, MIME type default, or the one selected by the user.
|
||||
*
|
||||
* This also adds the temporary file to the "To be deleted on Exit" list, if
|
||||
* the corresponding user preference is set (except on OS X).
|
||||
*
|
||||
* This function was adopted from nsExternalAppHandler::OpenWithApplication
|
||||
* (uriloader/exthandler/nsExternalHelperAppService.cpp).
|
||||
*/
|
||||
nsresult OpenWithApplication();
|
||||
|
||||
nsDownloadManager *mDownloadManager;
|
||||
nsCOMPtr<nsIURI> mTarget;
|
||||
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Download Manager Test Code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Edward Lee <edward.lee@engineering.uiuc.edu>.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by declaring the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not declare
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function run_test()
|
||||
{
|
||||
// We're testing migration to this version from one version below
|
||||
var targetVersion = 7;
|
||||
|
||||
// First import the downloads.sqlite file
|
||||
importDatabaseFile("v" + (targetVersion - 1) + ".sqlite");
|
||||
|
||||
// Init the download manager which will try migrating to the new version
|
||||
var dm = Cc["@mozilla.org/download-manager;1"].
|
||||
getService(Ci.nsIDownloadManager);
|
||||
var dbConn = dm.DBConnection;
|
||||
|
||||
// Check schema version
|
||||
do_check_true(dbConn.schemaVersion >= targetVersion);
|
||||
|
||||
// Make sure all the columns are there
|
||||
var stmt = dbConn.createStatement(
|
||||
"SELECT name, source, target, tempPath, startTime, endTime, state, " +
|
||||
"referrer, entityID, currBytes, maxBytes, mimeType, " +
|
||||
"preferredApplication, preferredAction " +
|
||||
"FROM moz_downloads " +
|
||||
"WHERE id = 28");
|
||||
stmt.executeStep();
|
||||
|
||||
// This data is based on the original values in the table
|
||||
var data = [
|
||||
"firefox-3.0a9pre.en-US.linux-i686.tar.bz2",
|
||||
"http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-trunk/firefox-3.0a9pre.en-US.linux-i686.tar.bz2",
|
||||
"file:///Users/Ed/Desktop/firefox-3.0a9pre.en-US.linux-i686.tar.bz2",
|
||||
"/Users/Ed/Desktop/+EZWafFQ.bz2.part",
|
||||
1192469856209164,
|
||||
1192469877017396,
|
||||
4,
|
||||
"http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-trunk/",
|
||||
"%2210e66c1-8a2d6b-9b33f380%22/9055595/Mon, 15 Oct 2007 11:45:34 GMT",
|
||||
1210772,
|
||||
9055595,
|
||||
// For the new columns added, check for null or default values
|
||||
true,
|
||||
true,
|
||||
0,
|
||||
];
|
||||
|
||||
// Make sure the values are correct after the migration
|
||||
var i = 0;
|
||||
do_check_eq(data[i], stmt.getString(i++));
|
||||
do_check_eq(data[i], stmt.getUTF8String(i++));
|
||||
do_check_eq(data[i], stmt.getUTF8String(i++));
|
||||
do_check_eq(data[i], stmt.getString(i++));
|
||||
do_check_eq(data[i], stmt.getInt64(i++));
|
||||
do_check_eq(data[i], stmt.getInt64(i++));
|
||||
do_check_eq(data[i], stmt.getInt32(i++));
|
||||
do_check_eq(data[i], stmt.getUTF8String(i++));
|
||||
do_check_eq(data[i], stmt.getUTF8String(i++));
|
||||
do_check_eq(data[i], stmt.getInt64(i++));
|
||||
do_check_eq(data[i], stmt.getInt64(i++));
|
||||
do_check_eq(data[i], stmt.getIsNull(i++));
|
||||
do_check_eq(data[i], stmt.getIsNull(i++));
|
||||
do_check_eq(data[i], stmt.getInt32(i++));
|
||||
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
|
||||
cleanup();
|
||||
}
|
Загрузка…
Ссылка в новой задаче