(-> % read write unlearn)

My writings on this area are my own delusion

MySQL 脳の自分のための PostgreSQL クエリ・メモ

PostgreSQL を使いだしてもう何年か経つのにいまだに MySQL 脳なのか、スキーマとデータベースはどっちがどっちか忘れがち。 → db > schema > table の階層関係。

[ PostgreSQL Instance ] (サーバー / クラスター)
       │
       ├── [ Database A ] (独立したデータ空間)
       │      │
       │      ├── < Schema: public > (デフォルト)
       │      │      ├── Table: users
       │      │      └── Table: orders
       │      │
       │      └── < Schema: inventory > (論理的なグループ)
       │             ├── Table: products
       │             └── Table: stocks
       │
       └── [ Database B ]
              └── < Schema: public >
                     └── Table: logs
graph TD
    Instance[PostgreSQL Instance]
    
    subgraph DB1 [Database: my_app]
        subgraph S1 [Schema: public]
            T1[Table: users]
            T2[Table: posts]
        end
        subgraph S2 [Schema: sales]
            T3[Table: invoices]
        end
    end

    subgraph DB2 [Database: analytics]
        subgraph S3 [Schema: public]
            T4[Table: raw_data]
        end
    end

    Instance --> DB1
    Instance --> DB2

MySQL 脳だと SHOW DATABASES; したくなる。そういうのはない。毎回ググるので一通りメモしておく。

1.データベース一覧

/*List Databases*/
\l+
/*List Databases*/
SELECT 
    datname AS database_name,
    pg_get_userbyid(datdba) AS owner,
    pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database
WHERE datistemplate = false;

2.スキーマ一覧

/*List Schemas*/
\dn+
/*List Schemas*/
SELECT schema_name 
FROM information_schema.schemata
WHERE schema_name NOT IN ('information_schema', 'pg_catalog') 
  AND schema_name NOT LIKE 'pg_toast%'
  AND schema_name NOT LIKE 'pg_temp%';

3.テーブル一覧

/*List Tables*/
\d
\ds     -- sequence のみ
\dt+    -- table のみ
/*List Tables*/
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_type = 'BASE TABLE'
  AND table_schema NOT IN ('information_schema', 'pg_catalog')
ORDER BY table_schema, table_name;
/*Change default search schema*/
SHOW search_path;
SET search_path=public;

4.カラム一覧

/*List Columns*/
\d TABLE_NAME
/*List Columns*/
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = 'TABLE_NAME'/*変更*/
ORDER BY ordinal_position;

それでは Happy database life!

Python のプロジェクトのタスクランナーをどうするか問題

nikkie-ftnext.hatenablog.com

nikkie-ftnext.hatenablog.com

どうやら、デファクトがないようす。Javascript の開発だと npm script が第一候補になる。

ツールに対する自分の偏見と選定基準

ツール 設定ファイル 偏見 Pros Cons
Make Makefile 王道。Python界隈でも昔からよく使われている 改めてインストールしなくても大抵の環境に入っている 書き方にハマりどころがある。そもそもビルドツールでありタスクランナーとしても使えるだけで、最適ではない。
Task Taskfile.yml Go 製。Goコミュニティ以外でも人気。 機能が豊富 とくにない
Just Justfile Makefileをタスクランナーとしていいとこどりした。 記法がシンプル、Makeに似てるので学習コストが低い。 独自記法なのでシンタックスハイライトがよくわからない。とりあえず Makefile と同じにしておいても大丈夫ではある。フォーマットは just --fmt がある。
taskipy pyproject.toml 内 Python Python製なので uv で入り追加のツールや設定ファイルが増えない とくにない
mise .mise.toml パッケージマネージャーだがタスクランナーの機能もある。 uv を mise で入れているなら、ツールを追加で入れる必要がない。 とくにない

選定基準

  • if, for loop, Y/n prompt, file watch など複雑なことをしたい → Task が絶対いい。
  • 追加のツールをインストールしなくても使えてほしい/ツールを増やしたくない → Makefile
    • uv を mise で管理してるなど既に mise を使ってる状況なら mise も選択肢。
    • Python のパッケージが増える分には気にしないなら taskipy も選択肢。
  • シンプルな記法が好き。Make に慣れてるけど Make より使いやすいもの → Just
  • YAML が好き → Task
  • TOML が好き → mise

個人的には、 Just のシンプルな記法が好きなので制約がなければ Just を使いたい。 しかし uv を mise 管理してることが多いのでツールが余計に増えないメリットが強すぎて mise を流用することが多い 1 。 複雑なことをしたいときにはスクリプトに書いてそれを呼び出すとするほうが好き。 そのためタスクランナーの中で機能性を必要とすることは少ない。 そのため Task はあまり選ばない。 taskipy 自体はPython完結だしいいツールなんだけど、mise が流用できる状況だと選ぶ理由があまりない。

Task ほどではなけど、 mise のタスクも細かく設定できてかゆいところに手が届くので不満はあまり出ない。

その他

uv がタスクランナー機能も出してきたらみんなそれを使いそうですね。


  1. 自分は既に慣れてるからよくても、新しく入ってくる人からしたら使ってるツールは1つでも少ないほうが楽だからね。

npm の pacakge-lock.json の仕様

Shai-Hulud サプライチェーン攻撃

npm に対する大規模なサプライチェーン攻撃「Shai-Hulud」について調査するなかで pacakge-lock.json の仕様について調べました。

codebook.machinarecord.com

npmプラットフォームを標的とした新たなサプライチェーン攻撃について、複数のセキュリティ企業が報告。自己伝播を行うワーム性能を特徴とするこの攻撃ではすでに187件のパッケージが侵害されており、その数は今後も増えていく恐れがあるという。

9月15日、SocketとStep Securityが最初にこの攻撃に関するレポート記事を公開。週あたりのダウンロード数220万超のパッケージ「@ctrl/tinycolor」に悪意あるアップデートが加えられていたこと、この感染はその他40以上のパッケージにも影響を与えているサプライチェーン攻撃の中で発生していたことなどが明かされていた。しかし16日、Aikido社はブログ記事の中で、さらに147件の侵害されたパッケージを観測したと報告。またこれらの感染パッケージの中には、セキュリティ大手CrowdStrikeのものも含まれると伝えた。

Socketによれば、侵害されたバージョンには関数「NpmModule.updatePackage」が含まれており、この関数によりパッケージのタールボールのダウンロード、package.jsonの修正、ローカルスクリプト「bundle.js」の注入、アーカイブのリパック、再公開が行われることで、「下流パッケージのトロイの木馬化」を自動化できるようになっているという。

副産物として pacakge-lock.json を jq でパースして目的のパッケージとバージョンがインストールされているか調べるスクリプトを作りました。

github.com

そのときに学んだ知見を吐き出します。

pacakge-lock.json

lockfileVersion

バージョンで構造が異なる。2025年10月現在は "lockfileVersion": 3

  1. v1: npm v5およびv6で使用されている。
  2. v2: npm v7およびv8で使用されている。v1形式と後方互換性がある。
  3. v3: npm v9以降で使用されている。npm v7との下位互換性がある。

npm v9 のリリースが 2022年なのでまだ3年しか経っていないので v2 以前もまだまだ普通に使われてそうですね。

https://github.blog/changelog/2022-10-24-npm-v9-0-0-released/

lodash を見るとまだ v1 ですね。

https://github.com/lodash/lodash/blob/main/package-lock.json#L4

v2 は、 v3 で必要な .packages キーにも依存パッケージ一覧の情報を冗長に保持しているので、今回の目的としては v2 もそれほど大きな違いはないようでした。

全体構造

以降では v3 を主に扱っていきます。例として gemini-cli の package-lock.json

https://github.com/google-gemini/gemini-cli/blob/main/package-lock.json

{
  "name": "@google/gemini-cli",
  "version": "0.9.0-nightly.20251001.163dba7e",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "@google/gemini-cli",
      "version": "0.9.0-nightly.20251001.163dba7e",
      "workspaces": [
        "packages/*"
      ],
      "dependencies": {
        "@testing-library/dom": "^10.4.1",
        "simple-git": "^3.28.0"
      },
      "bin": {
        "gemini": "bundle/gemini.js"
      },
      "devDependencies": {
        "@types/marked": "^5.0.2",
        "@types/mime-types": "^3.0.1",
        // 省略
        "vitest": "^3.2.4",
        "yargs": "^17.7.2"
      },
      "engines": {
        "node": ">=20.0.0"
      },
      "optionalDependencies": {
        "@lydell/node-pty": "1.1.0",
        "@lydell/node-pty-darwin-arm64": "1.1.0",
        "@lydell/node-pty-darwin-x64": "1.1.0",
        "@lydell/node-pty-linux-x64": "1.1.0",
        "@lydell/node-pty-win32-arm64": "1.1.0",
        "@lydell/node-pty-win32-x64": "1.1.0",
        "node-pty": "^1.0.0"
      }
    },
    "node_modules/@a2a-js/sdk": {
      "version": "0.3.2",
      "resolved": "https://registry.npmjs.org/@a2a-js/sdk/-/sdk-0.3.2.tgz",
      "integrity": "sha512-maqxdZ/xeuSRywObfBTvwXbXvkDMmKVkiY8K9rCHDwm0QYUJuu512GnNrwuxkKTwXpNyByzEPg3RYfBveRl96w==",
      "dependencies": {
        "uuid": "^11.1.0"
      },
      "engines": {
        "node": ">=18"
      },
      "peerDependencies": {
        "express": "^4.21.2"
      },
      "peerDependenciesMeta": {
        "express": {
          "optional": true
        }
      }
    },
// 省略

.packages

.packages にオブジェクトとして依存の一覧が書かれています。ツリー状ではなくほぼフラットです。

.packages.""

.packages オブジェクト内の空文字のキーは特殊なキーで直接的な依存パッケージ、つまり package.json に記述されたものが一覧で書かれます。

  • .packages."".dependencies
  • .packages."".devDependencies
  • .packages."".optionalDependencies

のように package.json での記述と同様のグルーピングがされています。

さて、インストールされているパッケージ名だけ一覧するならこんな感じ。

$ cat package-lock.json | jq '.packages | keys' | head 
[
  "",
  "node_modules/@a2a-js/sdk",
  "node_modules/@a2a-js/sdk/node_modules/uuid",
  "node_modules/@alcalzone/ansi-tokenize",
  "node_modules/@alcalzone/ansi-tokenize/node_modules/ansi-styles",
  "node_modules/@alcalzone/ansi-tokenize/node_modules/is-fullwidth-code-point",
  "node_modules/@ampproject/remapping",
  "node_modules/@asamuzakjp/css-color",
  "node_modules/@azu/format-text",

インストールされた理由が間接依存だったりする場合には、この記述がより長くなります。 例えば string-width パッケージはいろんなところから依存されているので次のような結果に。

$ cat package-lock.json | jq '.packages | keys' | grep 'string-width"'
  "node_modules/@textlint/linter-formatter/node_modules/string-width",
  "node_modules/ansi-align/node_modules/string-width",
  "node_modules/cli-truncate/node_modules/string-width",
  "node_modules/cliui/node_modules/string-width",
  "node_modules/ink/node_modules/string-width",
  "node_modules/listr2/node_modules/string-width",
  "node_modules/string-width",
  "node_modules/table/node_modules/string-width",
  "node_modules/update-notifier/node_modules/string-width",
  "node_modules/wrap-ansi-cjs/node_modules/string-width",
  "node_modules/wrap-ansi/node_modules/string-width",
  "node_modules/yargs/node_modules/string-width",
  "packages/cli/node_modules/string-width",

.packages."" 以外の要素

.packages."" 以外のキーは "node_modules/@a2a-js/sdk" のように、パッケージ名になっており、バリューはオブジェクトで、詳細を保持しています。

例えば .packages."node_modules/@a2a-js/sdk".version には、インストールしているパッケージのバージョン が書かれています。

$ jq '.packages."node_modules/@a2a-js/sdk".version' package-lock.json
"0.3.2"

$ cat package-lock.json | jq '.packages.[] | .version' | head
"0.9.0-nightly.20251001.163dba7e"  # これはこの gemini-cli 自体のバージョン .packages."" に入っている。
"0.3.2"
"11.1.0"
"0.2.0"
"6.2.1"
"5.0.0"
"2.3.0"
"3.2.0"
"1.0.2"
"1.0.1"

ざっくり 〜〜 パッケージはどんなバージョンがインストールされているんだろう、というのは次のようになります。

$ cat package-lock.json | jq '.packages'| grep 'string-width": {' -A1
  "node_modules/@textlint/linter-formatter/node_modules/string-width": {
    "version": "4.2.3",
--
  "node_modules/ansi-align/node_modules/string-width": {
    "version": "4.2.3",
--
  "node_modules/cli-truncate/node_modules/string-width": {
    "version": "7.2.0",
--
  "node_modules/cliui/node_modules/string-width": {
    "version": "4.2.3",
--
  "node_modules/ink/node_modules/string-width": {
    "version": "7.2.0",
--
  "node_modules/listr2/node_modules/string-width": {
    "version": "8.1.0",
--
  "node_modules/string-width": {
    "version": "5.1.2",
--
  "node_modules/table/node_modules/string-width": {
    "version": "4.2.3",
--
  "node_modules/update-notifier/node_modules/string-width": {
    "version": "7.2.0",
--
  "node_modules/wrap-ansi-cjs/node_modules/string-width": {
    "version": "4.2.3",
--
  "node_modules/wrap-ansi/node_modules/string-width": {
    "version": "7.2.0",
--
  "node_modules/yargs/node_modules/string-width": {
    "version": "4.2.3",
--
  "packages/cli/node_modules/string-width": {
    "version": "7.2.0",

さらにこのバージョンで入っているかな、まで見たかったら雑に grep すればいいでしょう。

その他の要素

その他の要素についても公式のドキュメントに網羅的に書かれています。

docs.npmjs.com

隠しロックファイル node_modules/.package-lock.json

node_modulesフォルダの処理を重複して行わないために node_modules/.package-lock.json に格納された「隠し」ロックファイルが存在します。パッケージツリーに関する情報が含まれておりルートの package-lock.json の代わりに npm から適宜使用されます。

package-lock.json との diff を取ってみると大部分は同じ内容ですが、いくつか違いがあります。その中の1つに

ideallyInert

パッケージオブジェクトに"ideallyInert" という Boolean の要素が追加されている、というのがあります。

この値は「理想的にはインストール対象外になってほしい(inert=非アクティブ扱い)パッケージ」であることを示すフラグだそうです。ググってもあまり情報がヒットしないので LLM に聞きました。

背景

npm はクロスプラットフォームなパッケージ(例: esbuild, sharp, fsevents など)を扱うとき、 CPU アーキテクチャや OS によって 使わないバイナリが node_modules に現れることがあります。

例: @esbuild/linux-arm → Linux ARM 向けのビルド macOS/x64 の環境では 必要ない が、npm レジストリには公開されているので解決ツリーに含まれる。

こうした「optional かつ環境依存で不要になる可能性が高い依存」は lockfile の packages セクションに残るけど、 実際にはインストール・利用されないことが多い。

そこで npm が ideallyInert: true を付けて、 「このパッケージは理想的には inert(非活性、インストール不要)扱いにしてほしい」という印を残す。

とのこと。

まとめ

  • package-lock.json の構造はバージョンによって異なるが、バージョン間で互換性も一部ある。最新は v3 (2022年〜)。
  • インストールされたパッケージとバージョンの一覧は、フラットに保持されているのでパースして探しやすい。
  • node_modules/.package-lock.json という隠しファイルがある。

注意点

単純にインストールされているパッケージのバージョンを調べたいとか、どういう経緯でインストールされているか調べたい場合は

などが使えます。今回は勉強を兼ねて直接パースしてみました。

また、 yarn や pnpm はロックファイルの名前もファイル形式(yaml)も構造もまったく異なるのでまた別途勉強しようと思います。