2025-12-04

NaviServer: Database

NaviServer 目前提供了二種資料庫介面, nsdb 與 後來擴充一些功能的 nsdbi。 nsdb 為 NaviServer 的內建模組(只是需要自行設定載入模組),而 nsdbi 需要使用者自己編譯並且加入到 NaviServer。

注意:如果想要使用 OpenACS 並且資料庫使用 PostgreSQL,那麼必須安裝 nsdbpg 模組。 下面使用 PostgreSQL 測試 nsdb 以及 nsdbpg 模組。使用者需要編譯 nsdbpg 模組並且加入到 NaviServer。 如果要自己編譯 nsdbpg 模組,注意其 Makefile 中 NAVISERVER 所設定的位置, 以及 dbpg.h 中 pg_config.h 和 libpq-fe.h 標頭檔的位置(openSUSE 安裝的位置放在 /usr/include/pgsql)。 下面是標示差異點的 patch 檔案。

--- dbpg.h.org    2025-12-03 16:37:40.171256167 +0800
+++ dbpg.h    2025-12-03 16:37:58.707205142 +0800
@@ -25,7 +25,7 @@
  * pg_config.h. However, the PACKAGE_* macros conflict with
  * NaviServer's packaging information, so we drop these.
  */
-#include <pg_config.h>
+#include <pgsql/pg_config.h>
 #undef PACKAGE_VERSION
 #undef PACKAGE_TARNAME
 #undef PACKAGE_STRING
@@ -34,7 +34,7 @@
 #undef PACKAGE_URL
 
 #include <nsdb.h>
-#include <libpq-fe.h>
+#include <pgsql/libpq-fe.h>
 
 /*
  * Forward compatibility, in case a new version of the module is compiled
--- Makefile.org    2025-12-03 16:39:07.524319290 +0800
+++ Makefile    2025-12-03 16:39:23.699037548 +0800
@@ -27,7 +27,7 @@
 # version of this file under either the License or the GPL.
 
 ifndef NAVISERVER
-    NAVISERVER  = /usr/local/ns
+    NAVISERVER  = /var/lib/naviserver
 endif
 
 #

不過除了直接修改,也可以使用設定參數的方式,下面就是編譯與安裝的指令:

make NAVISERVER=/var/lib/naviserver PGLIB=/usr/lib64 PGINCLUDE=/usr/include/pgsql
sudo make NAVISERVER=/var/lib/naviserver install

在安裝完 nsdbpb 以後,接下來修改 nsd-config.tcl,首先加入相關的變數:

# For database
dict set defaultConfig db_name           danilo
dict set defaultConfig db_user           danilo
dict set defaultConfig db_password       danilo
dict set defaultConfig db_host           localhost
dict set defaultConfig db_port           5432

加入 nsdb 模組,讓 NaviServer 在啟動時會載入模組。

ns_section ns/server/default/modules {
    if {$nscpport ne ""} {ns_param nscp nscp}
    ns_param    nslog               nslog    
    ns_param    nscgi               nscgi
    ns_param    nsperm              nsperm
    ns_param    nsdb                nsdb
    ns_param    revproxy            tcl
}

然後加入 nsdbpg driver 的設定。

ns_section ns/db/drivers {
    ns_param postgres nsdbpg
}

接下來加入 connection pool 的設定。使用者可以設定不止一個 pool,我在這裡只是驗證如何設定,所以只加入一個。

ns_section ns/server/default/db {
   #ns_param Pools pool1,pool2
   ns_param Pools pool1
   ns_param defaultpool pool1
}

ns_section ns/db/pools {
    ns_param pool1 "This is pool1 for PostgreSQL"
}

ns_section ns/db/pool/pool1 {
   ns_param Connections        10
   ns_param LogMinDuration     10ms
   ns_param LogSQLerrors       false
   ns_param driver             postgres
   ns_param DataSource         ${db_host}:${db_port}:${db_name}
   ns_param user               $db_user
   ns_param password           $db_password
}

最後寫一個簡單的測試程式測試。

<%
     ns_adp_puts "NaviServer nsdbpg module - get PostgreSQL version<br>"

     set db [ns_db gethandle]
     set result [ns_db 1row $db "select version() as version"]
     ns_adp_puts [ns_set get $result version]
%>

下面嘗試安裝 nsdbi 以及 nsdbipg。

首先是 nsdbi,下面就是編譯與安裝的指令:

make NAVISERVER=/var/lib/naviserver
sudo make NAVISERVER=/var/lib/naviserver install

再來是 nsdbipg,下面就是編譯與安裝的指令:

make NAVISERVER=/var/lib/naviserver PGLIB=/usr/lib64 PGINCLUDE=/usr/include/pgsql
sudo make NAVISERVER=/var/lib/naviserver install

接下來修改 nsd-config.tcl,首先是加入模組(有二種方式,一種是全域的加入方法,一種是 per server 的加入方式, 這裡使用後一種):

ns_section ns/server/default/modules {
    if {$nscpport ne ""} {ns_param nscp nscp}
    ns_param    nslog               nslog
    ns_param    nscgi               nscgi
    ns_param    nsperm              nsperm
    ns_param    revproxy            tcl
    ns_param    nsdbipg1            nsdbipg
}

以及 nsdbipg1 的設定:

ns_section ns/server/default/module/nsdbipg1 {
    ns_param   default        true ;# This is the default pool
    ns_param   handles        10   ;# Max open handles to db.
    ns_param   maxwait        10   ;# Seconds to wait if handle unavailable.
    ns_param   maxidle        0    ;# Handle closed after maxidle seconds if unused.
    ns_param   maxopen        0    ;# Handle closed after maxopen seconds, regardless of use.
    ns_param   maxqueries     0    ;# Handle closed after maxqueries SQL queries.
    ns_param   checkinterval  600  ;# Check for idle handles every 10 minutes.

    ns_param   datasource     "user='danilo' password='danilo' dbname='danilo'"
}

最後寫一個簡單的測試程式測試。

<%
     ns_adp_puts "NaviServer nsdbipg module - get PostgreSQL version<br>"

     dbi_1row -array result {select version() as version}
     ns_adp_puts $result(version)
%>

要注意的是,database handle 由 NaviServer 所管理,所以你在測試程式並沒有看到開啟 handle 的動作。

NaviServer: CGI and ADPs

NaviServer 本身支援 ASP/PHP 類似的寫法,可以內嵌 Tcl 程式碼到網頁中, 稱為 NaviServer Dynamic Pages (ADPs),其副檔名為 adp,可以參考下列的網頁:
NaviServer ADP Development

主要的語法有以下三種,第一種為使用 script 標籤:

<script language="tcl" runat="server" stream="on">
...
</script>

第二種則是短標籤:

<% ... %>

第二種則是印出變數的短標籤:

<%= ... %>

NaviServer 已經內建 nscgi 模組。檢查 nsd-config.tcl(或者是你使用的設定檔),確定有載入模組:

ns_section ns/server/default/modules {
    if {$nscpport ne ""} {ns_param nscp nscp}
    ns_param    nslog               nslog
    ns_param    nscgi               nscgi
    ns_param    nsperm              nsperm
    ns_param    revproxy            tcl
}

如果想要使用以前使用 Tcl 寫的 CGI 程式、或者是要使用 NaviServer 練習撰寫 CGI 程式, 只要注意 NaviServer cgi-bin 設定的位置正確的放置檔案,以及正確的加入 Tcl interpreter 的設定即可。 接下來修改 nsd-config.tcl:

ns_section ns/server/default/module/nscgi {
    ns_param    map                 "GET  /cgi-bin $home/cgi-bin"
    ns_param    map                 "POST /cgi-bin $home/cgi-bin"
    ns_param    interps              CGIinterps
    #ns_param   allowstaticresources true    ;# default false; serve static resources from cgi directories
}
    
ns_section ns/interps/CGIinterps {
    ns_param    .pl                 "/usr/bin/perl"
    ns_param    .tcl                "/usr/bin/tclsh"
    ns_param    .sh                 "/bin/bash"
}

2025-11-17

Tcl/Tk 9.0.3

Tcl/Tk 釋出了一個 9.0 的維護版本,9.0.3,說明檔案看起來是從 9.0.2 改的(似乎沒有改完全),不過公告看起來是沒問題的。

Tcl/Tk 9.0.3 的主要改變可以參考 tcl-release-notes-9.0.3.md, tk-release-notes-9.0.3.md

2025-10-21

tcljsonnet v0.18

首頁:
tcljsonnet


主要更新:
將 Jsonnet code base 版本升到 v0.21.0 版。Jsonnet 在 v0.21.0 這一版正式整合進 Rapid YAML(使用 single file 的方式),具有分析 YAML 的能力。在 v0.21.0 版之前開發的版本 Jsonnet 就已經開始加入 Rapid YAML,只是還沒有採用 single file 的方式,使用 single file 這個方式對於我來說比較好整合程式碼。

2025-10-19

tcl.mqttc v0.19

tcl.mqttc

這版整合了 paho.mqtt.c-1.3.15 的相關檔案。至於 C23 中 bool 關鍵字的解法,paho.mqtt.c 使用了更簡單的解法,直接將變數名稱改掉,這樣就可以避免掉問題。

2025-07-06

Tcl/Tk 9.0.2

Tcl/Tk 釋出了一個 9.0 的維護版本,9.0.2。

Tcl/Tk 9.0.2 的主要改變可以參考 tcl-release-notes-9.0.2.md, tk-release-notes-9.0.2.md

2025-06-19

tcl.mqttc v0.18

tcl.mqttc 

GCC15 C 編譯器如果沒有使用 -std 選項指定,編譯時使用的標準為 C23,bool 在 C23 成為關鍵字,所以自定義 bool 的做法在 GCC15 會編譯失敗。目前上遊還沒有確定解法,我只是選一個比較簡單的改法先讓程式能夠編譯成功,然後再看上遊會怎麼修改再進行修正。

#if defined __STDC__ && defined __STDC_VERSION__ && __STDC_VERSION__ <= 201710L
 typedef unsigned int bool;
#endif

也就是使用 __STDC_VERSION__ 進行判斷。

2025-06-18

tablelist 7.6

tablelist 釋出了 v7.6, Scrollutil 則釋出了 v2.6 版,這二個套件都是可以同一個作者,可以在作者的網站下載。另外,tablelist 和 Scrollutil 也是 Tklib 的套件,所以也可以下載最新的 Tklib source code 取得這二個套件。

2025-02-04

Valkey database

在更新 thiredis 之後,需要測試 thiredis 的關係,所以需要一個 Redis 協定相容的資料庫,openSUSE 目前採用的是 Valkey,所以筆記一下他的安裝部份。


Valkey is an open-source in-memory storage, used as a distributed, in-memory key–value database, cache and message broker, with optional durability.

在 openSUSE 安裝的指令:

sudo zypper in valkey

而下面是 README.SUSE 的簡要內容。需要使用 root 的權限進行設定。

  1. cp -a /etc/valkey/default.conf.example /etc/valkey/instancename.conf

    使用 -a 是因為要保留檔案的 permissions 設定,如果沒有使用,也可以使用下列的指令設定:
    chown root:valkey /etc/valkey/sentinel-instancename.conf
    chmod u=rw,g=rw,o= /etc/valkey/sentinel-instancename.conf

  2. change at least pidfile, logfile and dir setting

    pidfile /run/valkey/instancename.pid
    logfile /var/log/valkey/instancename.log
    dir /var/lib/valkey/instancename/

  3. create the database dir:
    install -d -o valkey -g valkey -m 0750 /var/lib/valkey/instancename/

  4. systemctl start valkey@instancename
  5. systemctl enable valkey@instancename
    (optional: 設定開機會啟動 valkey)

  6. To stop/restart all instances at the same time use:
    systemctl restart valkey.target
    systemctl stop valkey.target

如果要查看目前的狀態,使用:

sudo systemctl status valkey@instancename

相關連結

2024-12-23

Tcl/Tk 9.0.1

Tcl/Tk 釋出了一個 9.0 的維護版本,9.0.1。

Tcl/Tk 9.0.1 的主要改變可以參考 tcl-release-notes-9.0.1.md, tk-release-notes-9.0.1.md