積ん読 2010/01/30

PostgreSQL は TRUNCATE もトランザクション内です。

タイトルの通りですが、PostgreSQL では、TRUNCATE TABLE も トランザクション内なので、ロールバックすると データは元に戻ります。

次のようになります。

postgres=# select * from test1;
 f1 | f2
----+----
 1  |  1
 2  |  2
(2 rows)

postgres=# begin;
BEGIN

postgres=# truncate table test1;
TRUNCATE TABLE

postgres=# select * from test1;
 f1 | f2
----+----
(0 rows)

postgres=# rollback;
ROLLBACK

postgres=# select * from test1;
 f1 | f2
----+----
 1  |  1
 2  |  2
(2 rows)

面白いのは、CREATE TABLE などもトランザクション内です。

postgres=# begin;
BEGIN

postgres=# create table test2 (f1 varchar(1), f2 int);
CREATE TABLE

postgres=# select * from test2;
 f1 | f2
----+----
(0 rows)

postgres=# rollback;
ROLLBACK

postgres=# select * from test2;
ERROR:  relation "test2" does not exist

当然、このトランザクションがコミットするまでは、 別のトランザクションからは test2 テーブルは見えません。

別のトランザクションが絡んでくると少しややこしくなります。

トランザクション A がトランザクション中にテーブルを作成しているときに 他のトランザクション B が同じテーブル名でテーブルを作成しようとすると トランザクション B は A の結果待ちになります。

トランザクション A がロールバックして テーブルの作成がキャンセルされた場合 トランザクション B がテーブルを作成できるので、 トランザクション B はトランザクションを継続できます。

しかし、トランザクション A がコミットして テーブルを作成が確定してしまった場合は、 トランザクション B はテーブルの作成ができないのため B 側は、次のエラーが発生します。

ERROR: duplicate key value violates 
       unique constraint "pg_type_typname_nsp_index"

DROP TABLE も同様のことが起こります。

あまりこういったケースはないと思いますが、 この辺りの動きは Oracle と異なるので注意が必要です。

透明 favicon.ico

IE が Web サイトにアクセスすると 勝手に favicon.ico を探すので favicon.ico がないと Web サーバのエラーログに favicon.ico を探したログが残ってしまいます。

対処方法として favicon.ico へのアクセスをログに書き込まないように 設定するか、favicon.ico を実際に設置する方法があります。

後者の方法で、ダミー用の透明な favicon.ico が 欲しかったので作ってみました。

透明な favicon.ico

必要があれば使ってください。

ただし、ブックマークに登録した場合、他のサイトが Web ブラウザのデフォルトのアイコンか、設置されている favicon.ico が 表示されている中で、自分のところだけ空欄になって表示されるので 違和感は、かなりあります。

sudo で パスワードを聞かれないようにする

sudo コマンドを使用するときに パスワードを聞かれないようにする設定のメモです。

sudo の設定ファイルは /etc/sudoers ですが 編集するときは、直接開くのではなく visudo というコマンドを使用します。

ALL     ALL=(ALL)       NOPASSWD: ALL

左から、ユーザ、ホスト、権限、コマンドなのですが、 コマンドに NOPASSWD: と付けることでパスワードが聞かれなくなります。

特定のユーザに対して 特定のコマンドのみを許可したい場合は 次のように記述します。

user1   ALL=(ALL)       NOPASSWD: /etc/init.d/httpd

これで user1 は httpd の実行ができるようになります。

複数ファイルの一括置換

Linux 複数のテキストファイルの文字列を一括で 置換するためのシェルスクリプトを作ってみました。

#!/bin/bash
# ==========================================================
#  複数ファイル一括置換コマンド
#  -----------------------------------------------------
#  create : 2010/01/21 Studio ODIN
#  update :
# ==========================================================

# --- init ---------------------------------------------
COMMAND_NAME=bulk_replace
CUR_DIR=`pwd`

# -- args ----------------------------------------------
TARGET_WORD=$1
REPLACE_WORD=$2
TARGET_DIR=$3

# -- args check ----------------------------------------
if [ "$TARGET_WORD" = "" ]; then
    echo "target word is nothing." >&2
    echo "Usage: $0 targetword replaceword [targetpath]" >&2
    exit 1
fi
if [ "$REPLACE_WORD" = "" ]; then
    echo "replace word is nothing." >&2
    echo "Usage: $0 targetword replaceword [targetpath]" >&2
    exit 1
fi
if [ "$TARGET_DIR" = "" ]; then
    TARGET_DIR=.
fi

# -- execute -------------------------------------------
grep -R -l "${TARGET_WORD}" "${TARGET_DIR}" | while read file
do
    tfile=`mktemp -u`
    cp -f -p $file $tfile > /dev/null 2>&1
    if [ $? -eq 0 ]; then
        sed -e "s/${TARGET_WORD}/${REPLACE_WORD}/g" $file > $tfile
        if [ $? -ne 0 ]; then
            echo "Warning: failed write '$file'"
        fi
        mv -f $tfile $file > /dev/null 2>&1
        if [ $? -ne 0 ]; then
            echo "Warning: failed rewrite '$file'"
            rm -f $tfile > /dev/null 2>&1
        fi
    else
        echo "Warning: failed copy '$file'"
    fi
done

# -- normal end ----------------------------------------
cd $CUR_DIR
exit 0

# ==========================================================
#  shell end
#  location: /usr/local/bin/bulk_replace
# ==========================================================

実行する場合は、パスの指定など大事なファイルが 大変なことにならないように気を付けてください。 (自己責任でお願いします)

こんなコマンドは、すでにあるような気もしますが 自分で処理を考えて作ってみるのは面白いと思います。

Ruby の Array#inject について

Ruby の認定試験を受ける後輩から Array#inject が わかりにくいという話を聞いたので 簡単にまとめてみたいと思います。

Array#inject は配列の値を合計したりするのに使える便利なメソッドです。

$ irb
irb(main):001:0> [1,2,3,4].inject(0) { | sum, i | sum + i }
=> 10

内部的には次のような処理になります。

[1,2,3,4].inject(0) { | sum, i | sum + i }
                  `-------,
        loop: 1           0, 1     0 + 1
                                   ~~|~~
                          ,----------'
        loop: 2           1, 2     1 + 2
                                   ~~|~~
                          ,----------'
        loop: 3           3, 3     3 + 3
                                   ~~|~~
                          ,----------'
        loop: 4           6, 4     6 + 4
                                   ~~|~~
                                     `----> 出力

2つめのブロック引数には配列の値がはいるのですが 1つめのブロック引数には、前回のループの最後の処理結果が入ります。

1巡目 (loop:1) の1つめのブロック引数は、 前回の処理結果がないので、初期値として inject メソッドの引数が設定されます。

ブロック内をわかりやすく書くと次のようになります。

irb(main):001:0> [1,2,3,4].inject(0) { | sum, i | sum += i }
=> 10

ここまでは簡単なのですが、この inject メソッドは 引数を省略することができます。 この場合の動きが要注意です。

上と同じ配列で試してみます。

irb(main):001:0> [1,2,3,4].inject { | sum, i | sum + i }
=> 10

結果は同じでした。

ここで、ブロック内の処理を変えてみます。

irb(main):001:0> [1,2,3,4].inject(0) { | sum, i | sum * i }
=> 0
irb(main):002:0> [1,2,3,4].inject { | sum, i | sum * i }
=> 24

まったく異なる結果になりました。

まず、引数がある場合の内部処理を見てみます。

[1,2,3,4].inject(0) { | sum, i | sum * i }
                  `-------,
        loop: 1           0, 1     0 * 1
                                   ~~|~~
                          ,----------'
        loop: 2           0, 2     0 * 2
                                   ~~|~~
                          ,----------'
        loop: 3           0, 3     0 * 3
                                   ~~|~~
                          ,----------'
        loop: 4           0, 4     0 * 4
                                   ~~|~~
                                     `----> 出力

次に、引数がない場合の内部処理を見てみます。

[1,2,3,4].inject { | sum, i | sum * i }
  `--------------------,
        loop: 1        1, 2     1 * 2
                                ~~|~~
                       ,----------'
        loop: 2        2, 3     2 * 3
                                ~~|~~
                       ,----------'
        loop: 3        6, 4     6 * 4
                                ~~|~~
                                  `----> 出力

このように 初期値として 配列の 1つめの値が入っています。 また、1巡目 (loop:1) で 配列の 2つめの値が入っています。 ループの数も 1つ少なくなります。 引数を省略すると、 配列の 1つめのを値を初期値として利用するわけです。

つまり、次の 2つが等価となります。

irb(main):001:0> [1,2,3,4].inject(0) { | sum, i | sum * i }
=> 0
irb(main):002:0> [0,1,2,3,4].inject { | sum, i | sum * i }
=> 0

ORDER BY expressions must appear in select list

次のような PostgreSQL の SQL エラーがあります。

SELECT DISTINCT, ORDER BY expressions must appear in select list

これは SELECT DISTINCT を使用している場合に、 ORDER BY 句に含まれている項目が SELECT 句にない場合に発生します。

例えば、次のような SQL です。

SELECT DISTINCT
       field_1
     , field_2
FROM test_table
ORDER BY field_3

SELECT DISTINCT されているのは field_1 と field_2 ですが ソートは field_3 を指定しています。

これでは結果の並び順を保証することができないため エラーが発生するわけです。

ちなみに Oracle の場合は、次のようなエラーになります。

ORA-01791: SELECT式が無効です

ただ、Oracle は さらに賢いようで、次のような場合はエラーになりません。 ( PostgreSQL はエラーになります)

SELECT DISTINCT
       field_1
     , field_2
FROM test_table
WHERE field_3 = '0'
ORDER BY field_3

この場合は、field_3 が 1つに固定されるため、 "ソート指定なし" と同じ状態になるためエラーになりません。

平成22年度春期試験 申込受付開始

情報処理推進機構:情報処理技術者試験:受験申込み

春の情報処理試験申込の受付が開始しました。

受験する人は忘れないうちに申し込みましょう。
(私は受けない予定だったりしますが・・・)

Ruby インストーラー Rumix を試してみる

Windows 環境に簡単に Ruby の実行環境を作るフリーソフトが紹介されていたので 試しにインストールしてみました。

Windowsでスクリプト言語“Ruby”を導入するための和製インストーラー「Rumix」

まず、以下のサイトから Rumix をダウンロードします。

Rumix - Ruby Starter Package with Installer

これを書いている時点では「 Rumix 1.00(通常版)」「 Rumix 1.00(ruby 1.9.1版) 」と ありますが、どちらが良いかわからない人は「 Rumix 1.00(通常版)」を落としましょう。

ダウロードしたファイルを解凍して、その中の rumix_install.exe を実行します。

「次へ」ボタンを押します。

WRITE_0197_01

「次へ」ボタンを押します。

WRITE_0197_02

「次へ」ボタンを押します。

WRITE_0197_04

「次へ」ボタンを押します。

WRITE_0197_06

「次へ」ボタンを押します。

WRITE_0197_07

「次へ」ボタンを押します。

WRITE_0197_08

「次へ」ボタンを押します。

WRITE_0197_09

「インストール」ボタンを押します。

WRITE_0197_10

終わるのを待ちます。

WRITE_0197_11

待ちます。

WRITE_0197_12

待ちます。

WRITE_0197_13

これでインストールは終了です。

次に ruby スクリプトを作って実行させてみます。

デスクトップに test.rb というファイルを作って テキストエディタで スクリプトを書きます。

puts 'hello world!'

スタートメニューから
「 NYACUS (コマンドラインシェル) 」を起動します。

WRITE_0197_14

[d:\]
$ 

NYACUS は Linux のシェルの操作ができる便利なツールです。

cd や ls, pwd などのコマンドも使えます。 ウィンドウにドラッグ&ドロップしてパスを指定することもできます。

[d:\]
$ cd d:\desktop
[d:\desktop]
$ pwd
d:\desktop

先ほど書いたスクリプトを実行させてみます。

WRITE_0197_15

"ruby test.rb" でも 直接指定でも実行させることができます。

簡単に ruby のスクリプトを実行できるので Ruby の試験勉強などにも とても役に立ちます。

Excel から UTF-8 で出力する

Excel のマクロなど UTF-8 で出力する モジュールを紹介します。

UTF-8ファイル作成 for VBA

PostgreSQL の テーブルレイアウトを Excel で作成して Excel のマクロで CREATE 文を生成しているのですが コメント文など 日本語を UTF-8 で出力するのに 利用させてもらいました。

使い方も簡単で、ダウンロードした class ファイルを Excel にインポートして、Open メソッドの代わりに 次のように記述します。

Dim f1 As New TextFile

f1.FileCreate "test1.txt", "UTF-8"
f1.TextWrite "このファイルは UTF-8 でエンコードされています。"
f1.FileClose

相対パスから絶対パスを取得する

Linux で realpath などが入っていない状況で 相対パスから絶対パスを取得するための シェルスクリプトです。

#!/bin/bash
# ==========================================================
#  絶対パス取得コマンド
#  -----------------------------------------------------
#  create : 2010/01/07 Studio ODIN
#  update :
# ==========================================================

# --- init ---------------------------------------------
COMMAND_NAME=fullpath
CUR_DIR=`pwd`

# -- args ----------------------------------------------
TARGET_OBJECT=$1

# -- args check ----------------------------------------
if [ "$TARGET_OBJECT" = "" ]; then
    pwd
    exit 0
fi
if [ ! -e "$TARGET_OBJECT" ]; then
    echo "Error: '$TARGET_OBJECT' is not exists." >&2
    exit 1
fi

# -- get full path (is directory) ----------------------
if [ -d $TARGET_OBJECT ]; then
    cd $TARGET_OBJECT
    pwd
    exit 0
fi

# -- get full path (is not directory) ------------------
cd `dirname $TARGET_OBJECT`
TARGET_PATH=`pwd`
TARGET_NAME=`basename $TARGET_OBJECT`
if [ "$TARGET_PATH" = "/" ]; then
    echo $TARGET_PATH$TARGET_NAME
else
    echo $TARGET_PATH/$TARGET_NAME
fi

# -- normal end ----------------------------------------
cd $CUR_DIR
exit 0

# ==========================================================
#  shell end
#  location: /usr/local/bin/fullpath
# ==========================================================

次の例は、fullpath という名前で /usr/local/bin に作成して実行権限を付けています。 環境に合わせて変更してください。

# cd /usr/local/bin
# vi fullpath
# chmod 755 fullpath

引数を指定しなければ、カレントディレクトリのパス、 ディレクトリやファイルを指定すれば、その絶対パスを返します。

$ fullpath
/home/odin

$ fullpath ../
/home

$ fullpath /
/

$ fullpath ../../vmlinuz
/vmlinuz

謹賀新年

あけましておめでとうございます。

初めての人も、そうで無い人も、 今年もよろしくお願いします。

Google サイト内検索

Amazonアソシエイト