0

現在、FTP サーバーから一連の .csv をダウンロードし、各 .csv を独自のテーブルとして MySQL データベースに配置するスクリプトを作成しています。

RCurl を使用して FTP から .csv をダウンロードし、すべての .csv を作業ディレクトリに配置します。各 .csv からテーブルを作成するために、テーブル名が .csv と同じ名前である RODBC パッケージの sqlSave 関数を使用しています。これは、.csv 名が 18 文字未満の場合は正常に機能しますが、それよりも大きい場合は失敗します。「失敗」とは、R がクラッシュすることを意味します。バグを追跡するために、sqlSave でデバッグを呼び出しました。

R をクラッシュさせる sqlSave が呼び出す関数が少なくとも 2 つあることがわかりました。1 つ目は RODBC:::odbcTableExists で、これは非表示の関数です。関数のコードは次のとおりです。

 RODBC:::odbcTableExists
function (channel, tablename, abort = TRUE, forQuery = TRUE, 
    allowDot = attr(channel, "interpretDot")) 
{
    if (!odbcValidChannel(channel)) 
        stop("first argument is not an open RODBC channel")
    if (length(tablename) != 1) 
        stop(sQuote(tablename), " should be a name")
    tablename <- as.character(tablename)
    switch(attr(channel, "case"), nochange = {
    }, toupper = tablename <- toupper(tablename), tolower = tablename <- tolower(tablename))
    isExcel <- odbcGetInfo(channel)[1L] == "EXCEL"
    hasDot <- grepl(".", tablename, fixed = TRUE)
    if (allowDot && hasDot) {
        parts <- strsplit(tablename, ".", fixed = TRUE)[[1]]
        if (length(parts) > 2) 
            ans <- FALSE
        else {
            res <- if (attr(channel, "isMySQL")) 
                sqlTables(channel, catalog = parts[1], tableName = parts[2])
            else sqlTables(channel, schema = parts[1], tableName = parts[2])
            ans <- is.data.frame(res) && nrow(res) > 0
        }
    }
    else if (!isExcel) {
        res <- sqlTables(channel, tableName = tablename)
        ans <- is.data.frame(res) && nrow(res) > 0
    }
    else {
        res <- sqlTables(channel)
        tables <- stables <- if (is.data.frame(res)) 
            res[, 3]
        else ""
        if (isExcel) {
            tables <- sub("^'(.*)'$", "\\1", tables)
            tables <- unique(c(tables, sub("\\$$", "", tables)))
        }
        ans <- tablename %in% tables
    }
    if (abort && !ans) 
        stop(sQuote(tablename), ": table not found on channel")
    enc <- attr(channel, "encoding")
    if (nchar(enc)) 
        tablename <- iconv(tablename, to = enc)
    if (ans && isExcel) {
        dbname <- if (tablename %in% stables) 
            tablename
        else paste(tablename, "$", sep = "")
        if (forQuery) 
            paste("[", dbname, "]", sep = "")
        else dbname
    }
    else if (ans) {
        if (forQuery && !hasDot) 
            quoteTabNames(channel, tablename)
        else tablename
    }
    else character(0L)
}

テーブル名の長さが 18 文字を超える場合、これは失敗します。

    res <- sqlTables(channel, tableName = tablename)

これを次のように変更して修正しました。

    res <- sqlTables(channel, tablename)

次に、assignInNamepace を使用してこのコード変更を行い、名前空間で同じ名前 (odbcTableExists) の関数を再割り当てします。

RODBC:::odbcTableExists が原因で問題が発生しなくなりました。ただし、sqlSave() 内から sqlwrite が呼び出されると、R は引き続きクラッシュします。sqlwrite でデバッグを呼び出したところ、テーブル名が長すぎると RODBC:::odbcColumns (別の非表示関数) がクラッシュすることがわかりました。残念ながら、以前のように RODBC:::odbcColumns を変更してバグを回避する方法がわかりません。

R 2.15.1 を使用しており、プラットフォームは x86_64-pc-ming32/x64 (64 ビット) です。また、これを仕事用のコンピューターで実行しようとしていることに注意してください。ただし、まったく同じコードを自分のコンピューターで実行しても、R はクラッシュしません (バグはありません)。職場のコンピューターは Windows 7 Professional を実行し、自宅のコンピューターは R 2.14.1 で Windows 7 ホーム プレミアムを実行します。

4

1 に答える 1

0

私はこのハックが大好きです (私も R 2.15.1 の Windows 7 Professional を使用しています)。もうクラッシュすることはありませんが、その行を置き換えて assignInNamespace を使用すると、別の問題が発生します。また、何らかの理由で、odbcValidChannel を RODBC:::odbcValidChannel に、quoteTabNames を RODBC:::quoteTabNames に置き換える必要がありました。

しかし、sqlSave を使用すると、次のエラーが発生しました。

odbcUpdate(channel, query, mydata, coldata[m, ], test = test, : パラメーターがないため、更新するものがありません

コードのどこにも odbcUpdate を使用していません。また、RODBC::: sqlSave には odbcUpdate 呼び出しが含まれていません。

何かご意見は?

ありがとう - アレックス

于 2012-09-21T21:58:01.683 に答える