PostgreSQLのTimestamp型をC言語アプリケーションで扱う
PostgreSQLの公式ドキュメントに記述がありますが、timestamp型はUNIX時刻のように、ある基点時刻からの経過秒数を表しています。
timestampの値は2000-01-01深夜を基準にした経過秒数として格納されます。
ということで、libpqを用いて取得されたtimestamp型の値に"2000-01-01T00:00:00ZのUNIX時刻"を足せばUNIX時刻に変換できます。
ただし、
timestampの値が8バイト整数(現在のデフォルト)で格納されていれば、すべての値についてμ秒精度が有効です。
との記述通り、timestamp型の値はマイクロ秒表現になっているので1000000で割ってから946684800(2000-01-01T00:00:00ZのUNIX時刻)を足すことでUNIX時刻に変換できます。
VPC内のLambdaからSQSに接続できない問題の解法
C/C++アプリケーションの64bit化対応
C/C++で書かれた業務アプリケーションを32bitから64bitへ移行させる機会があったのでやったことや観点など残しておきます。
環境
OS
Windows Server 2008 R2 → Windows Server 2016
コンパイラ
Visual Studio 2012 → Visual Studio 2017
修正の観点
プログラムは下記観点に沿ってチェック、修正しました。
データ型のサイズ変更
データ型のサイズは、データモデルというものによって分けられます。
C/C++は規格上、整数型のビット幅は固定ではなく、相対的に決まっています。
short <= int <= long <= long long という感じです。
データモデルごとに整数型のビット幅をまとめたのが以下。
データモデル | short | int | long | long long | pointer | 処理系 |
---|---|---|---|---|---|---|
LLP64 | 16 | 32 | 32 | 64 | 64 | Windows 64bit |
LP64 | 16 | 32 | 64 | 64 | 64 | 一般的なUnix系64bit |
ILP32 | 16 | 32 | 32 | 64 | 32 | 一般的な32bit |
その他にもデータモデルはありますが、割愛。
表を見てわかる通り、Windows 32bitからWindows 64bitへの移行は、ポインタのサイズが変わります。綺麗に作ってあるプログラムなら問題ないのですが、intやlongにアドレス値を格納してたりするとバグります。
ただ、intやlongにアドレス値を代入しようとすると、コンパイラが警告を出してくれると思います。Visual Studio (VC++) 2017でももちろん警告してくれます。
構造体パディング変更
構造体やクラスをメモリ上に配置する際、配置の仕方を工夫することでアクセス速度があがります。コンパイラがこの工夫のために追加するのがパディングです。
Cの規格上、構造体メンバのメモリ配置の順番は宣言順になることが求められますが、メンバとメンバの間にパディングを挿入することが許されています。パディングはメンバとメンバの間および最後のメンバの後に挿入されます。
メンバ間パディング
後ろのメンバのオフセット(構造体先頭からの位置)が型サイズの倍数となるように挿入されます。
最終メンバ後パディング
構造体全体のサイズが、構造体メンバの中で最も大きい型サイズの倍数となるように挿入されます。
つまり
64bitに移行するとパディングが変わる可能性があります。Cでよくやる、バイナリの電文をそのまま構造体にキャストみたいな使い方だとパディングの差でバグります。
実例あげようと思いましたが面倒なので割愛。
その他コンパイラ警告
セキュリティが強化されている関数を使用していないという警告が多数出ました。
strncpy()の代わりにstrncpy_s()を使え、memcpy()の代わりにmemcpy_s()を使えといった警告です。_s()のついた関数は、異常時に例外を発生させるため注意が必要です。基本的に関数名だけ変えて引数そのままだと挙動が変わります。
実数型への対応
これは64bit化というよりもハードウェア移行の影響です。CPUが変わると実数型の計算方法が変わる可能性があるとのこと。今回修正対象のアプリケーションは座標計算を行うため、修正の前後で計算結果にずれが発生すると問題になります。すべての座標計算をテストすることで確認しました。特定の計算結果を比較するだけで実数計算の誤差を確かめるような方法があればよかったのですが、わかりませんでした。
2038年問題への対応
時刻をtime_t型で扱っていれば64bit化で2038年問題もクリアされるのですが、残念ながらint型で時刻を扱っている箇所がありました。時刻計算をする際に一時的にint型変数にキャストされるといったケースも要注意です。
最後に
32bit時代のアプリケーションを64bit化するのは大変でした。