2017-05-19

Tcl extension and TDBC driver for CrateDB database : cratedb-tcl 0.1

Source code:
cratedb-tcl


如果需要知道 CrateDB 的一些資料:
CrateDB


因為 CrateDB REST API 支援 Parameter Substitution,所以實際上可以用來模擬 prepare/execute 的行為,這個套件為了驗證這個想法而寫的。

也就是實際上是使用 TclCurl 發出 POST request,然後取得結果。而為了模擬,所以我們將步驟分為幾個部份:
  • prepare: 取得 SQL code,只是放在某個變數中
  • param: 使用者設定參數的型別 (TYPE) 與值 (VALUE)
  • 當使用者呼叫 execute,我們將前面二者的資料結合並且設定好 Parameter Substitution 以後,發出 POST request 然後取得結果
而因為模擬了 prepare/execute,所以我們就可以使用 TDBC 中的 ::tdbc::tokenize 分析 SQL 並且進行參數代換的工作,這部份完成以後,就有一個基本可用的 TDBC driver。

但是需要注意的是因為是用模擬的,跟一般資料庫的行為還是有一些差距,當設定  PARAM 時如果沒有為某個欄位指定 TYPE/VALUE,一般會設為 NULL,雖然 TDBC 的部份我有做一些處理,但是還是沒辦法跟一般的資料庫行為完全一樣。

讓我驚嚇的部份是 CrateDB 欄位部份會自動按照變數 欄位的字母大小排序,也就是在 insert 時不指定欄位順序的話,就會出現插入資料到錯誤的欄位。我反覆測試的時候發現的,目前不知道是 CrateDB 的設計還是 bug。

TclCurl DELETE request (using -customrequest)

package require TclCurl

proc httpBlobDelete {url} {
    set curlHandle [curl::init]
    $curlHandle configure -url $url -customrequest DELETE
    catch { $curlHandle perform } curlErrorNumber
    if { $curlErrorNumber != 0 } {
        return -code error [curl::easystrerror $curlErrorNumber]
    }
    
    set responsecode [$curlHandle getinfo responsecode]
    $curlHandle cleanup
    
    if {$responsecode == 204} {
        return -code ok
    } elseif {$responsecode == 404} {
        return -code error "Not Found"
    } else {
            return -code error "ERROR"
    }
}

用來測試 CrateDB REST API 中 BLOB delete 的程式。

如果 TclCurl 要指定 DELETE request,使用 -customrequest 來指定。因為 CrateDB 會回傳 status code 來表示檔案已成功刪除 (204),或者是沒有這個檔案 (404),所以取得 responsecode 以後判斷。大致上是這樣。


更新:
CrateDB 加入 BLOB 資料使用 PUT,下面是測試的程式:
package require TclCurl

proc httpBlobPut {url data} {
    set curlHandle [curl::init]
    $curlHandle configure -url $url -customrequest PUT -postfields $data
    catch { $curlHandle perform } curlErrorNumber
    if { $curlErrorNumber != 0 } {
        return -code error [curl::easystrerror $curlErrorNumber]
    }
    
    set responsecode [$curlHandle getinfo responsecode]
    $curlHandle cleanup
    
    if {$responsecode == 201} {
        return -code ok
    } elseif {$responsecode == 409} {
        return -code error "Conflict"
    } else {
            return -code error "ERROR"
    }
}


使用 GET 讀取內容:
proc httpBlobGet {url} {
    set curlHandle [curl::init]
    $curlHandle configure -url $url -bodyvar html
    catch { $curlHandle perform } curlErrorNumber
    if { $curlErrorNumber != 0 } {
        return -code error [curl::easystrerror $curlErrorNumber]
    }
    
    set responsecode [$curlHandle getinfo responsecode]
    $curlHandle cleanup
    
    if {$responsecode == 200} {
        return -code ok $html
    } elseif {$responsecode == 404} {
        return -code error "Not Found"
    } else {
            return -code error "ERROR"
    }
}

2017-05-14

info library

info library

info library returns the name of the library directory in which standard Tcl scripts are stored.

我忘記我有沒有記錄過這件事,就是 openSUSE 和 Ubuntu 如果你使用 info library 取得資訊,openSUSE (64 位元) 是在 /usr/lib64/tcl/tcl8.6,但是 Debian/Ubuntu 使用不同的方式來組織 32/64 位元目錄,所以會拿到不同的資訊。

如果是純粹 Tcl 所撰寫的套件,openSUSE 也有可能會放在 /usr/share/tcl 這個目錄下面。

2017-05-12

tclcubrid v0.9.5

Source code

tclcubrid

About


CUBRID is an open source SQL-based relational database management system with object extensions developed by Naver Corporation for web applications.

tclcubrid is a Tcl extension by using CUBRID CCI (CCI Client Interface) driver to connect CUBRID. I write this extension to research CUBRID and CCI (CCI Client Interface) driver.

Main changelog

  1. Improve set/multiset/sequence type handle for fetch_row_list/fetch_row_dict method.
    Now tclcubrid will return a Tcl list for set/multiset/sequence type.
    It is an INCOMPATIBILITY change! User maybe need update source code.
  2. Add blob/clob write size check

改進對於 set/multiset/sequence type 的處理。同時加入更多 blob/clob size 的檢查。


更新:
CUBRID 網站怪怪的,我本來想指向正確的文件位址,結果位址一直變動。最後我覺得煩了,我把位址暫時清掉,然後也重置了  tclcubrid 的 git repository。

更新:
早上起來發現網站又回復為原來的樣子,所以我把連結加回去了,希望接下來不會又出現連結失效的問題。

2017-05-03

TclCurl and YQL

我不太確定 YQL 是不是還有在維護,不過這是用來測試 TclCurl Http GET 的程式,送出查詢給 Yahoo Query Language (YQL),然後取得結果。

package require Tcl 8.6
package require TclOO
package require TclCurl

oo::class create CurlYQL {
    variable html_result
    
    constructor {} {
        set html_result ""
    }
    
    destructor {
    }    
    
    method query {query args} {
        variable url
        variable pairs

        set url "https://query.yahooapis.com/v1/public/yql"
        set pairs {}

        lappend pairs "[curl::escape format]=[curl::escape json]"
        lappend pairs "[curl::escape q]=[curl::escape $query]"
        
        foreach {name value} $args {
            lappend pairs "[curl::escape $name]=[curl::escape $value]"
        }
    
        append url ? [join $pairs &]

        set curlHandle [curl::init]
        $curlHandle configure -url $url -bodyvar [namespace current]::html_result
        catch { $curlHandle perform } curlErrorNumber
        if { $curlErrorNumber != 0 } {
            error [curl::easystrerror $curlErrorNumber]
        }

        $curlHandle cleanup
    }
    
    #
    # get the results
    #
    method getResults {} {
        return $html_result
    }
}

所以接下來就是測試。

送出查詢到 Yhaoo Finance (for CSV file) 查詢 SPY, VOO, IVV 的股價,我們拿到結果以後,使用 rl_json 分析結果並且印出來。

package require rl_json

set curlyql [CurlYQL new]
$curlyql query {select * from csv where url='http://download.finance.yahoo.com/d/quotes.csv?s=SPY,VOO,IVV&f=sl1d1t1c1ohgv&e=.csv' and columns='symbol,price,date,time,change,col1,high,low,col2'}
set query_result [$curlyql getResults]

if {[::rl_json::json exists $query_result query]==1} {
    set rows [::rl_json::json get $query_result query results row]

    puts "========================================\n"
    foreach row $rows {        
        puts "symbol: [dict get $row symbol]"
        puts "price: [dict get $row price]"
        puts "date: [dict get $row date]"
        puts "time: [dict get $row time]"
        puts "change: [dict get $row change]"
        puts "col1: [dict get $row col1]"
        puts "high: [dict get $row high]"
        puts "low: [dict get $row low]"
        puts "col2: [dict get $row col2]"
        puts "========================================\n"
    }
}

$curlyql destroy

* 注意:rl_json 新增加的 command json 放在 namespace rl_json 內,不過使用說明上沒有特別明講,所以需要注意一下這件事。

* 更新:
我把資料放到 Github 上了。

2017-04-29

package vcompare

可以看 Wiki 的解釋。

Compares the two version numbers given by version1 and version2. Returns -1 if version1 is an earlier version than version2, 0 if they are equal, and 1 if version1 is later than version2.


實際上的用法可以看 tcllib 中 zipfile::decode module。

if {[package vcompare $tcl_patchLevel "8.6"] < 0} {
  # Only needed pre-8.6
  package require Trf                       ; # Wrapper to zlib
  package require zlibtcl                   ; # Zlib usage. No commands, access through Trf
  set ::zipfile::decode::native_zip_functs 0
} else {
  set ::zipfile::decode::native_zip_functs 1
}

也就是如果 >= 8.6,使用內建的 zlib,如果小於就使用 Trf 與 zlibtcl。

接下來我檢查了一下 zipfile::decode module,發現 zipfile::decode module 並沒有修改完全,::zipfile::decode::GetFile 中沒有使用 native_zip_functs 來選擇要使用的 command。下面是修正的 patch。

     deflate {
         go     $fd(fileloc)
         nbytes $fd(csize)
-        return [zip -mode decompress -nowrap 1 -- [getval]]
+        if {$::zipfile::decode::native_zip_functs} {
+          return [zlib inflate [getval]]
+        } else {
+          return [zip -mode decompress -nowrap 1 -- [getval]]
+        }
     }
     default {
         Error "Unable to handle file \"$src\" compressed with method \"$fd(cm)\"" \



我已經在 Tcl Library Source Repository 開了一個 Ticket 來記錄這件事。

2017-04-14

tclmpi v1.0

目前在 Ubuntu 14.04 上測試過。

Ubuntu 14.04 需要安裝 openmpi,指令如下:
sudo apt-get install openmpi-bin libopenmpi-dev


然後從 https://github.com/akohlmey/tclmpi 或者是 http://sites.google.com/site/akohlmey/software/tclmpi 下載 v1.0 source code。

接下來需要修改 Makefile,讓 tcl.h 可以被正確找到。
TCLINCLUDE=-I/usr/include/tcl

修改完 Makefile 以後,執行 make,然後在 /usr/lib 建立 tclmpi1.0 目錄,將 pkgIndex.tcl, tclmpi.tcl 與 _tclmpi.so 三個檔案複製到 /usr/lib/tclmpi1.0,這樣就完成安裝。

再來使用 examples 下的 hello.tcl 來測試,如果有正確輸出,就表示安裝正確。

package require tclmpi 1.0

# initialize TclMPI
::tclmpi::init

set comm tclmpi::comm_world
set size [::tclmpi::comm_size $comm]
set rank [::tclmpi::comm_rank $comm]

puts "hello world, this is rank $rank of $size"

# close out TclMPI
::tclmpi::finalize
exit 0

2017-04-09

TclTLS-rpm-spec

TclTLS-rpm-spec
TclTLS website


參考 openSUSE 網站上的 RPM spec 以後,做一些小修改造出 1.7.11 的 RPM  檔案。看起來是可以使用,但是只有小量的測試而已,我不知道會不會有什麼問題。

然後寫一個 build.tcl 來加速整個流程。

2017-03-03

zookeepertcl

zookeepertcl

Flightaware 最近在 github 增加了 zookeepertcl,Tcl interface to Apache ZooKeeper distributed coordination system。


如果要寫一個 CP 分散式系統,就我聽過的,在一致性上目前有三個方法可以使用,實作正確的 Raft Consensus Algorithm,實作正確的 Paxos Algorithm 以及使用 Apache Zookeeper。

雖然 Apache Zookeeper 是用 Java 寫的,但是其一致性算法已經經過證明而且十分可靠,所以我想有個 Tcl interface 還是不錯的。


更新:
如果你對 Apache ZooKeeper 的測試報告有興趣,可以閱讀 Jepsen: Zookeeper。

2017-02-22

tclreadline and .tclshrc

tclreadline

flightaware clone 了一份 tclreadline 的 source code。如果在 Ubuntu 14.04 下編譯,aux 目錄下會缺少東西,需要使用 libtoolize & aclocal & autoconf & automake --add-missing 來更新。

下面只是嘗試設定 tclsh 提示。

if {$tcl_interactive} {
    package require tclreadline 

    proc ::tclreadline::prompt1 {} {
        global env
        if {[catch {set pwd [pwd]} tmp]} {
            set pwd "unable to get pwd"
        }

        if [info exists env(HOME)] {
            regsub $env(HOME) $pwd "~" pwd
        }


        if [info exists env(USER)] {
            set user $env(USER)
        } else {
            set user tclsh
        }

        return "$user@[lindex [split [info hostname] "."] 0]:$pwd% "
    }

    ::tclreadline::Loop
}



更新:
同時也在 openSUSE 上測試 tclreadline,為了可以快速安裝和移除,參考了其它人的做法以後,使用 RPM spec 和 build script 來造 RPM 檔案。