PCのキーボードで端末を操作する

Androidアプリの開発をする場合、PCに接続したAndroid端末にビルドしたアプリを転送して動作を確認することが少なくない。
アプリを開発してみるとわかると思うが、微調整段階になるとその転送して確認することが頻繁になり、PCのキーボードから手を離すのが面倒になってくる。

そこでPCからAndroid端末を操作できるアプリはないものかと探したところ、「Wifi Keyboard」というのが見つかり、世の中にはあるものだと関心してしまった。

早速使ってみたところ、Bluetoothキーボードと同様に何事もなくAndroid端末が操作できる。
Wifiだけではなく、ADK付属のadbコマンドを使えば、USB接続でも利用できる。
おまけに、PCの日本語入力がそのまま利用できる。

ただし、Terminal IDEではCtrlキーが効かないので、相性が悪いアプリが存在するようだ。

ある人の記事では笑いがしばらく止まらないと評価していたが、その通りだと思うので、是非試してみて欲しい。

iOS8は重いから、色々と調整!

iOS8になったら、iPod Touch 5のレスポンスが悪くなった。
特に入力の開始直後は文字を打っても、画面に出るまで数秒かかることが頻繁に発生するようになった。
iOSはメジャーバージョンアップする度に必ず遅くなる。
最新のデバイスでしか動作検証していないものと想像している。
iPod touchユーザには勘弁して欲しい点だ。

文句を言っても始まらないので、設定で軽減することにした。

  • Siriをオフ
  • Handoffと候補のApp以下をすべてオフ
  • キーボードの音声入力、自動大文字入力、自動修正、予測、ピリオドの簡易入力をオフ
  • Appのバックグラウンド更新で不要なものをオフ
  • iCloudと連携しないアプリをオフ
  • プライバシーの位置情報サービスを使用しないアプリ、システムサービスのSpotlightの検索候補、時間帯の設定、利用頻度の高い位置情報 、この近くで人気、診断/使用状況をオフ
  • プライバシーの診断/使用状況で自動送信をオフ
  • 通知の不要なアプリをオフ
  • 一般のSpotlight検索をすべてオフ

これで大分軽くなった。

Terminal IDEでアプリ開発!?(その2)

前回はサンプルのビルドを行ったが、今回は世の中にアップされているオープンライブラリを使用して、簡単なアプリを開発してみようと思う。
実際に使用するのは、テスティングフレームワークで有名なJUnitである。

JARファイルをDEXファイルに変換

Android端末ではJARファイルのオープンライブラリをそのまま使用できないので、DEXファイル(Dalvik仮装マシンのバイトコード)に変換する必要がある。
メモり不足となるので、残念ながらTerminal IDE上では変換できないため、PC上で変換する。
実際の変換はdxコマンドを使用する。

今回使用したAndroid SDK 23.0.2では、dxコマンドは以下に存在する。

ZIP展開先\android-sdk\build-tools\20.0.0\dx.bat

以下のコマンドを実行し、JUnitライブラリをDEXファイルに変換する。

dx --dex --no-srtict --output=junit-4.11.dex.jar junit-4.11.jar
dx --dex --no-srtict --output=hamcrest-core-1.3.dex.jar hamcrest-core-1.3.dex.jar

JARファイルとDEXファイルをAndroid端末にコピーする。
コピーしたファイルをTerminal IDEにおいて「~/work/libs/」以下に配置する。

サンプルプログラムの作成

以下のサンプルプログラムを「~/work/src/」に作成する。

public class Thing
{
    private String name;	// 3 characters or over
    public String getName()
    {
        return this.name;
    }
    public void setName(String newName)
    {
        if (newName == null || newName.length() < 3) {
            throw new IllegalArgumentException("illegal name");
        }
        this.name = newName;
    }
}
import org.junit.Test;
import static org.junit.Assert.*;

public class ThingTest
{
    @Test public void instanciate()
    {
        Thing thing = new Thing();
        assertNull(thing.getName());
    }
    @Test public void setNameOk()
    {
        Thing thing = new Thing();
        thing.setName("abc");
        assertEquals(thing.getName(), "abc");
    }
    @Test public void setNameOk2()
    {
        Thing thing = new Thing();
        thing.setName("鱗田太郎");
        assertEquals(thing.getName(), "鱗田太郎");
    }
    @Test public void setNameNull()
    {
        boolean hasError = false;
        Thing thing = new Thing();
        try {
            thing.setName(null);
        }
        catch (IllegalArgumentException e) {
            hasError  = true;
        }
        assertEquals(hasError, true);
    }
    @Test public void setNameNullString()
    {
        boolean hasError = false;
        Thing thing = new Thing();
        try {
            thing.setName("");
        }
        catch (IllegalArgumentException e) {
            hasError  = true;
        }
        assertEquals(hasError, true);
    }
    @Test public void setNameTooShort()
    {
        boolean hasError = false;
        Thing thing = new Thing();
        try {
            thing.setName("12");
        }
        catch (IllegalArgumentException e) {
            hasError  = true;
        }
        assertEquals(hasError, true);
    }
}

Makefileの作成

makeコマンドでビルド&実行を行うために、以下のMakefileを「~/work/src/」に作成する。

CLASSPATH=../libs/junit-4.11.jar:../libs/hamcrest-core-1.3.jar

CLASSFILES=Thing.class ThingTest.class

DEXFILE=Thing.dex.jar

.SUFFIXES: .class .java

.java.class:
    javac -cp $(CLASSPATH) $<

all: $(DEXFILE) run

$(DEXFILE): $(CLASSFILES)
    dx --dex --verbose --no-strict --output=$@ $(CLASSFILES)

LIBFILES=../libs/junit-4.11.dex.jar:../libs/hamcrest-core-1.3.dex.jar

MAIN=org.junit.runner.JUnitCore

ARGS=ThingTest

run:
    java -jar $(LIBFILES):$(DEXFILE) $(MAIN) $(ARGS) 

makeコマンドで実行

makeコマンドを実行すると、必要に応じてコンパイルが行われ、ビルドされた単体テストが実行される。

$ cd ~/work/src
$ make
javac -cp ../libs/junit-4.11.jar:../libs/hamcrest-core-1.3.jar Thing.java
javac -cp ../libs/junit-4.11.jar:../libs/hamcrest-core-1.3.jar ThingTest.java
dx --dex --verbose --no-strict --output=Thing.dex.jar Thing.class ThingTest.class
processing Thing.class...
processing ThingTest.class...
writing classes.dex; size 1972...
java -jar ../libs/junit-4.11.dex.jar:../libs/hamcrest-core-1.3.dex.jar:Thing.dex.jar org.junit.runner.JUnitCore ThingTest 
JUnit version 4.11
......
Time: 0.013

OK (6 tests)

$

最後に

コンパイルが思ったよりも遅いが、耐えられないほどではない。
コンパイル時はJARファイル、実行時はDEXファイルを必要とする点さえ理解していれば、オープンソースのライブラリを簡単に取り入れることができる。
これでモバイルの開発環境を間違いなく手に入れたと思った。

Terminal IDEでアプリ開発?!(その1)

Android端末のみでアプリ開発ができると言うTerminal IDEで、実際に何がどこまで出来るのかを試してみた。
結論から言うと、C、C++Javaのコンソールアプリと、JavaAndroidアプリが開発できるようだ。
また、bashawkなどがあるので、ある程度のスクリプトも書ける。

インストール

インストールは以下の3段階の手順となる。
なお、現時点のバージョンは2.02である。

  1. アプリ本体のインストール
    AppStoreからTerminal IDE本体をインストールする。
  2. システムのインストール
    Terminal IDEの初期画面で「Install System」をタップし、次の画面で再度「Install System」をタップする。
  3. gccのインストール
    Terminal IDEの初期画面で「Terminal IDE」をタップし、「system/bin/install_gcc」を実行する。

オススメの設定

最低限、以下の設定をオススメする。

  1. 付属のソフトウェアキーボードを使用
    コンソール操作ではTabキーやCtrlキーなどの特殊キーを何かと使用するが、多くのAndroid用ソフトウェアキーボードではそれらの特殊キーを打つことができないので、Terminal IDEに付属のソフトウェアキーボードに変更する。
  2. フォントサイズの変更
    デフォルトの12ポイントでは少し小さいので14ポイント程度にオプションで変更する。
  3. プロンプトの簡略化
    画面が狭いため、「~/.bashrc」で定義されているプロンプトの環境変数を変更する。
    PS1='\w\$ '
  4. vim使用時はBACKキーをESCキーに変更
    ハードウェアキーボードのESCキーは戻るボタンに割り当てられているが、vimではコマンドモードに移行するため、ESCキーとして動作するようにメニューで変更する。

サンプルのビルド

「~/system/src/」以下にサンプルが保存されているので、これらをビルドしてみる。
なお、README.txtを読めば、概ねのことが分かる。

  • 「c_examples/」以下は次の通りである。
    • 「chello/」以下では「make」を実行すると、「hello」がビルドできる。
    • 「cpphello/」以下では「make」を実行すると、「rect」がビルドできる。
    • 「clib/」以下では「make all install」を実行すると、「capp/」以下で利用するライブラリがビルドできる。
    • 「capp/」以下では「make」を実行すると、「tester」がビルドできる。
  • 「helloworld/」以下では「./builder.sh」を実行すると、「hello.jar」がビルドできるので、これを「./run.sh」で実行できる。
  • 「demo_lib/」以下では「./builder.sh」を実行すると、「dist/」に「demolib.jar」や「demolib.dex.jar」がビルドできる。
    これらは既に「demo_console/libs/」や「demo_android/libs/」に保存されている。
  • 「demo_console/」以下では「./builder.sh」を実行すると、「dist/」に「demo_console.dex.jar」がビルドできるので、これを「./run.sh」で実行できる。
  • 「demo_android/」以下では「./builder.sh」を実行すると、「dist/」に「demo_android.apk」や「demo_android_signed.apk」がビルドできる。ただし、「./run.sh」や「./install.sh」の実行にはSuperSUの「su」コマンドを使用するなどで、root権限を取得する必要がある。

その他の情報

「~/system/bin/bbdir」にはBusyBoxに含まれているコマンドがソフトリンクとして保存されているので、一度確認してみて欲しい。
例えば、telnetdを実行すれば、PCからTELNETで接続できるので便利である。

Bluetooth接続の英語キーボードを刻印通りに打つ

以前、写真のASUS MeMO Pad HD 7 (ME173X)対応のジャケット付きBluetoothキーボードを購入したのだが、キートップの刻印通りに打てないキーが存在する。

この問題を解決した時の経緯を以下に記載する。

なお、以下に記載のコマンドはAndroid上のTerminal IDEのコンソールで実行した。
コマンドの実行に必要なroot権限はSuperSUの「su」コマンドで取得した。

バイス名の取得

バイス名は設定の「言語と入力」の「物理キーボード」に以下が表示されている。

  hid-keyboard

バイス情報の取得

バイス情報は「dumpsys input」コマンドで得られる。

INPUT MANAGER (dumpsys input)

Event Hub State:
  BuiltInKeyboardId: -2
  Devices:
    7: hid-keyboard
      Classes: 0x0000014b
      Path: /dev/input/event5
      Descriptor: e6829a71d080dbf7cc1317c4cc5515a23611afd2
      Location: 
      UniqueId: 
      Identifier: bus=0x0019, vendor=0x0000, product=0x0000, version=0x0000
      KeyLayoutFile: /system/usr/keylayout/hid-keyboard.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic-ja_JP.kcm
      ConfigurationFile: /system/usr/idc/hid-keyboard.idc
      HaveKeyboardLayoutOverlay: true

キーコードの取得

上記「デバイス情報」の「Path」から、不正な文字が表示されるキーのキーコードを「getevent /dev/input/event5」コマンドで取得した。

刻印 表示 キーコード(10進数)
[ @ 26
] [ 27
\ ] 43
| } 42 43
{ @ 42 26
} { 42 27
~ なし 42 41
` 半角/全角 41

※上記の「42」は左シフトキー

キーと文字の割り当て

上記「デバイス情報」の「Generic-ja_JP.kcm」を見ると、以下の記述があるため、今回の現象が発生していることが分かる。

map key 26 AT
map key 27 LEFT_BRACKET
map key 41 ZENKAKU_HANKAKU
map key 43 RIGHT_BRACKET

IDCファイルの変更

上記「デバイス情報」の「hid-keyboard.idc」を以下のように変更した。

【変更前】
keyboard.layout = hid-keyboard
keyboard.characterMap = Generic-ja_JP

【変更後】
keyboard.layout = hid-keyboard
keyboard.characterMap = Generic

実際の変更は以下の手順で行う。

  1. 「mount /emmc@android /system -o remount,rw」コマンドで「/system」を書き込み可能に変更する。
  2. 「hid-keyboard.idc」を書き換える。
  3. タブレットを再起動する。

「User-installable keymaps」について

Android 4.1から、キーと文字の割り当てをユーザが定義できる仕組みが追加され、ユーザ定義は設定の「言語と入力」の「物理キーボード」をタップすると表示される「キーボードレイアウトの選択」で指定することができる。
今回の問題にはこの方法で対応しようか迷ったのだが、「Generic-ja_JP.kcm」で一旦定義されたものを、再度元の「hid-keyboard.kl」で定義されたものに戻すのが納得できなかったので、前述のIDCファイルを変更する方法を採用した。

クラスのフィールドやメソッドを表示する

Apache Commons Langのorg.apache.commons.lang3.builderパッケージには、必要に応じてオーバーライドする以下のObjectクラスのメソッドを実装するためのヘルパークラスが存在する。

メソッド ヘルパークラス 説明
public boolean equals(Object o) EqualsBuilder 等価確認
public String toString() ToStringBuilder 文字列変換
public int hashCode() HashCodeBuilder ハッシュ値変換
public int compareTo(Object o) CompareToBuilder 大小比較

上記のToStringBuilderを使用した一般的なtoStringメソッドの実装方法は以下となる。

import org.apache.commons.lang3.builder.*;

public class MyClass
{
    private String str1 = "hello world";
    private int num1 = 123;
    private double val1 = 456.789;

    @Override
    public String toString()
    {
        return ToStringBuilder.reflectionToString(this);
    }

    public static void main(String[] args)
    {
        MyClass myInst = new MyClass();
        System.out.println(myInst.toString());
    }
}

上記の実行結果から以下の出力が得られる。

MyClass@7ea987ac[str1=hello world,num1=123,val1=456.789]

上記の出力結果を見て、ToStringBuilder.reflectionToStringメソッドはどのようにしてフィールド名を列挙し、その値を得ているのだろうかと不思議に思ったので、調べて見ることにした。

Classクラスの役割

Object.getClassメソッドを使用すると、Classクラスのインスタンスを得ることができる。
Classクラスはクラスをモデル化するクラスであるが、このClassクラスのメソッドを使用して、インスタンス化されたクラスのフィールドやメソッドを列挙することができる。

【オブジェクトのフィールド名と値の表示】
    public void printFields(Object o)
    {
        for (Field f : o.getClass().getDeclaredFields()) {
            System.out.println(f.getType().toString() + " " + f.getName() + " = " + f.get(o).toString() + ";");
        }
    }
【オブジェクトのメソッド名とパラメータの列挙】
    public void printMethods(Object o)
    {
        for (Method m : o.getClass().getDeclaredMethods()) {
            System.out.print(m.getReturnType().getName() + " " + m.getName() + "(");
            String comma = "";
            for (Class<?> c : m.getParameterTypes()) {
                String name = c.getName();
                if (name.charAt(0) == '[') {
                    switch (name.charAt(1)) {
                    case 'B' :
                        name = "byte";
                        break;
                    case 'S' :
                        name = "short";
                        break;
                    case 'I' :
                        name = "int";
                        break;
                    case 'J' :
                        name = "long";
                        break;
                    case 'C' :
                        name = "char";
                        break;
                    case 'F' :
                        name = "float";
                        break;
                    case 'D' :
                        name = "double";
                        break;
                    case 'L' :
                        name = name.substring(2, name.length() - 1);
                        break;
                    default :
                        name = name.substring(1);
                        break;
                    }
                    name += "[]";
                }
                System.out.print(comma + name);
                comma = ", ";
            }
            System.out.print(");\n");
        }
    }

終わりに

ObjectクラスやClassクラスは重要な基本クラスなので、これらについて学ぶことはJavaを深く知るために必要なことだと思った。

JDK付属のDerbyを試して見る

「スッキリわかるJava入門 実践編」の第9章「データベースアクセス」には、Apache DerbyはJava 6から標準添付されていると記載されていたので、実際に試して見た。

PATH環境変数の設定

現在、PCにインストールされているのはJava 8で、以下のフォルダにインストールされている。

JDKインストールフォルダ
C:\Program Files\Java\jdk1.8.0_05
JREインストールフォルダ
C:\Program Files\Java\jre1.8.0_20

JDKのインストールフォルダを確認すると、以下のフォルダが存在するのが分かる。

Derbyフォルダ
C:\Program Files\Java\jdk1.8.0_05\db

先ずはコマンドプロンプトでDerbyのコマンドを実行しやすいように、PATH環境変数に以下のパスを追加する。

Derbyコマンドフォルダ
C:\Program Files\Java\jdk1.8.0_05\db\bin

初期設定の確認

コマンドプロンプトを開いて、以下のコマンドを実行する。
なお、カレントディレクトリは「C:\work」としたので、予め同フォルダが存在しているものとする。

C:\work> sysinfo
------------------ Java情報 ------------------
Javaバージョン:    1.8.0_20
Javaベンダー:     Oracle Corporation
Javaホーム:       C:\Program Files\Java\jre1.8.0_20
Javaクラスパス:    C:\Program Files\Java\jdk1.8.0_05\db\bin\../lib/derby.jar;C:\Program Files\Java\jdk1.8.0_05\db\bin\../lib/derbynet.jar;C:\Program Files\Java\jdk1.8.0_05\db\bin\../lib/derbyclient.jar;C:\Program Files\Java\jdk1.8.0_05\db\bin\../lib/derbytools.jar
OS名:           Windows 7
OSアーキテクチャ: amd64
OSバージョン:     6.1
Javaユーザー名:    pochi
Javaユーザー・ホーム: C:\Users\pochi
Javaユーザー・ディレクトリ:   C:\work
java.specification.name: Java Platform API Specification
java.specification.version: 1.8
java.runtime.version: 1.8.0_20-b26
--------- Derby情報 --------
[C:\Program Files\Java\jdk1.8.0_05\db\lib\derby.jar] 10.10.1.3 - (1557168)
[C:\Program Files\Java\jdk1.8.0_05\db\lib\derbytools.jar] 10.10.1.3 - (1557168)
[C:\Program Files\Java\jdk1.8.0_05\db\lib\derbynet.jar] 10.10.1.3 - (1557168)
[C:\Program Files\Java\jdk1.8.0_05\db\lib\derbyclient.jar] 10.10.1.3 - (1557168)
------------------------------------------------------
----------------- ロケール情報 ----------------
現行ロケール:  [日本語/日本 [ja_JP]]
ロケールのサポートが見つかりました: [cs]
	 バージョン: 10.10.1.3 - (1557168)
ロケールのサポートが見つかりました: [de_DE]
	 バージョン: 10.10.1.3 - (1557168)
ロケールのサポートが見つかりました: [es]
	 バージョン: 10.10.1.3 - (1557168)
ロケールのサポートが見つかりました: [fr]
	 バージョン: 10.10.1.3 - (1557168)
ロケールのサポートが見つかりました: [hu]
	 バージョン: 10.10.1.3 - (1557168)
ロケールのサポートが見つかりました: [it]
	 バージョン: 10.10.1.3 - (1557168)
ロケールのサポートが見つかりました: [ja_JP]
	 バージョン: 10.10.1.3 - (1557168)
ロケールのサポートが見つかりました: [ko_KR]
	 バージョン: 10.10.1.3 - (1557168)
ロケールのサポートが見つかりました: [pl]
	 バージョン: 10.10.1.3 - (1557168)
ロケールのサポートが見つかりました: [pt_BR]
	 バージョン: 10.10.1.3 - (1557168)
ロケールのサポートが見つかりました: [ru]
	 バージョン: 10.10.1.3 - (1557168)
ロケールのサポートが見つかりました: [zh_CN]
	 バージョン: 10.10.1.3 - (1557168)
ロケールのサポートが見つかりました: [zh_TW]
	 バージョン: 10.10.1.3 - (1557168)
------------------------------------------------------

PHPのと混同する名前のコマンドで紛らわしいが、とにかく、JavaとDerbyの情報が表示される。

チュートリアル

以下のサイトに記載の手順にしたがってチュートリアルを実行して見る。

Derbyチュートリアル Step 2
http://db.apache.org/derby/papers/DerbyTut/ij_intro.html
[Create a database]
C:\work>ij
ijバージョン10.10
ij> connect 'jdbc:derby:MyDbTest;create=true';
ij> exit;
C:\work>type derby.log
----------------------------------------------------------------
Mon Sep 15 12:52:01 JST 2014:
DerbyバージョンThe Apache Software Foundation - Apache Derby - 10.10.1.3 - (1557168): インスタンスa816c00e-0148-7770-4ce1-000006e860f8を
file:/C:/Program%20Files/Java/jdk1.8.0_05/db/lib/derby.jarからロードされたクラス・ローダーsun.misc.Launcher$AppClassLoader@66d3c617により
データベース・ディレクトリC:\work\MyDbTest上でブートしています
java.vendor=Oracle Corporation
java.runtime.version=1.8.0_20-b26
user.dir=C:\work
os.name=Windows 7
os.arch=amd64
os.version=6.1
derby.system.home=null
データベース・クラス・ローダーが開始されました - derby.database.classpath=''
----------------------------------------------------------------
Mon Sep 15 12:52:08 JST 2014: Derbyエンジンを停止しています
----------------------------------------------------------------
Mon Sep 15 12:52:08 JST 2014:
クラス・ローダーsun.misc.Launcher$AppClassLoader@66d3c617を持つデータベース・ディレクトリC:\work\MyDbTestのインスタンスa816c00e-0148-7770-4ce1-000006e860f8を停止しています
----------------------------------------------------------------

上記の通り、「C:\work\MyDbTest」にデータベースフォルダが作成され、その下に各種データベースファイルが保存されていることが確認できる。

[Connect to a database]
C:\work>ij
ijバージョン10.10
ij> connect 'jdbc:derby:MyDbTest';
[Execute SQL statements]
ij> create table derbyDB(num int, addr varchar(40));
0行が挿入/更新/削除されました
ij> insert into derbyDB values (1956,'Webster St.');
1行が挿入/更新/削除されました
ij> insert into derbyDB values (1910,'Union St.');
1行が挿入/更新/削除されました
ij> update derbyDB set num=180, addr='Grand Ave.' where num=1956;
1行が挿入/更新/削除されました
ij> select * from derbyDb;
NUM        |ADDR
----------------------------------------------------
180        |Grand Ave.
1910       |Union St.

2行が選択されました
[Disconnect from a database]
ij> disconnect;
[Exit]
ij> exit;
C:\work>
[Run SQL Scripts]

予め、以下のSQLファイルを作成する。

ファイル名
my_file.sql
connect 'jdbc:derby:MyDbTest';
create table OpenDB(name varchar(20),url varchar(50));
insert into OpenDB values ('MySQL', 'http://www.mysql.com');
insert into OpenDB values ('PostgreSQL', 'http://www.postgresql.org');
insert into OpenDB values ('sqlite', 'http://www.sqlite.org');
insert into OpenDB values ('Apache Derby', 'http://db.apache.org/derby');
select * from OpenDB;
disconnect;

以下のrunコマンドを実行すると、

C:\work>ij
ijバージョン10.10
ij> run 'my_file.sql';

想定通り、手入力と同様にSQLコマンドが実行される。

ij> connect 'jdbc:derby:MyDbTest';
ij> create table OpenDB(name varchar(20),url varchar(50));
0行が挿入/更新/削除されました
ij> insert into OpenDB values ('MySQL', 'http://www.mysql.com');
1行が挿入/更新/削除されました
ij> insert into OpenDB values ('PostgreSQL', 'http://www.postgresql.org');
1行が挿入/更新/削除されました
ij> insert into OpenDB values ('sqlite', 'http://www.sqlite.org');
1行が挿入/更新/削除されました
ij> insert into OpenDB values ('Apache Derby', 'http://db.apache.org/derby');
1行が挿入/更新/削除されました
ij> select * from OpenDB;
NAME                |URL
-----------------------------------------------------------------------
MySQL               |http://www.mysql.com
PostgreSQL          |http://www.postgresql.org
sqlite              |http://www.sqlite.org
Apache Derby        |http://db.apache.org/derby

4行が選択されました
ij> disconnect;

終わりに

「スッキリわかるJava入門」には章末に練習問題が掲載されているが、第9章「データベースアクセス」の練習問題は予めテーブルが作成されていることが前提の問題だったので、他のDBMSと同様にSQLコマンドを実行するコマンドを使用して、練習問題で必要となるテーブルを作成しようと考えた。
コマンドの命名がイマイチだが、とにかく、ツールが揃っていて良かった。