sql >> データベース >  >> RDS >> SQLite

既存のデータベースの異なるバージョンを管理するために使用できる方法はどれですか?

    簡単に言うと、新しいアセットファイルを適切な場所にコピーし、元のデータベースファイルのコピーを保持しながら、更新を適用して、ユーザーのデータを新しくコピーしたデータベースに効果的に保存することができます。 しかし その特定の変更のためにアプリがアップグレードされており、データベースが存在する場合のみ。

    データベースが存在しない場合は、アセットファイルの標準コピーを実行する必要があります。

    これを行う方法の例を次に示します。

    この例は、アセットフォルダ内のファイルやデータベースファイル自体の管理と問い合わせを可能にするルーチンに大きく依存しています。

    クラス、つまり DBAssetHandler.java 上記に対応します(また、SQLiteOpenHelperを使用する場合は、user_version、つまりデータベースバージョンを抽出する機能もあります)。

    • このクラスはAndroidPieでもテストされているため、ログ先行書き込み(PieのデフォルトのWAL)と、以前のデフォルトのジャーナルモードに対応していることに注意してください。

    • また、WALを使用する場合は、データベースが完全にチェックポイントされていることを確認する必要があることにも注意してください。「ログ先行書き込み」を参照してください。

    それは:-

    public class DBAssetHandler {
    
        static final String[] tempfiles = new String[]{"-journal","-wal","-shm"}; // temporary files to rename
        public static final String backup = "-backup"; //value to be appended to file name when renaming (psuedo delete)
        public static final  int OUCH = -666666666;
    
        /**
         * Check if the database already exists. NOTE will create the databases folder is it doesn't exist
         * @return true if it exists, false if it doesn't
         */
        public static boolean checkDataBase(Context context, String dbname) {
    
            File db = new File(context.getDatabasePath(dbname).getPath()); //Get the file name of the database
            Log.d("DBPATH","DB Path is " + db.getPath()); //TODO remove if publish App
            if (db.exists()) return true; // If it exists then return doing nothing
    
            // Get the parent (directory in which the database file would be)
            File dbdir = db.getParentFile();
            // If the directory does not exits then make the directory (and higher level directories)
            if (!dbdir.exists()) {
                db.getParentFile().mkdirs();
                dbdir.mkdirs();
            }
            return false;
        }
    
    
        /**
         * Copy database file from the assets folder
         * (long version caters for asset file name being different to the database name)
         * @param context           Context is needed to get the applicable package
         * @param dbname            name of the database file
         * @param assetfilename     name of the asset file
         * @param deleteExistingDB  true if an existing database file should be deleted
         *                              note will delete journal and wal files
         *                              note doen't actually delete the files rater it renames
         *                              the files by appended -backup to the file name
         *                              SEE/USE clearForceBackups below to delete the renamed files
         */
        public static void copyDataBase(Context context, String dbname, String assetfilename, boolean deleteExistingDB) {
    
            final String TAG = "COPYDATABASE";
            int stage = 0, buffer_size = 4096, blocks_copied = 0, bytes_copied = 0;
            File f = new File(context.getDatabasePath(dbname).toString());
            InputStream is;
            OutputStream os;
    
            /**
             * If forcing then effectively delete (rename) current database files
             */
            if (deleteExistingDB) {
                //String[] tempfiles = new String[]{"-journal","-wal","-shm"};
                //String backup = "-backup";
                f.renameTo(context.getDatabasePath(dbname + "-backup"));
                for (String s: tempfiles) {
                    File tmpf = new File(context.getDatabasePath(dbname + s).toString());
                    if (tmpf.exists()) {
                        tmpf.renameTo(context.getDatabasePath(dbname + s + backup));
                    }
                }
            }
    
    
            //Open your local db as the input stream
            Log.d(TAG,"Initiated Copy of the database file " + assetfilename + " from the assets folder."); //TODO remove if publishing
            try {
                is = context.getAssets().open(assetfilename); // Open the Asset file
                stage++;
                Log.d(TAG, "Asset file " + assetfilename + " found so attmepting to copy to " + f.getPath()); //TODO remove if publishing
    
                os = new FileOutputStream(f);
                stage++;
                //transfer bytes from the inputfile to the outputfile
                byte[] buffer = new byte[buffer_size];
                int length;
                while ((length = is.read(buffer)) > 0) {
                    blocks_copied++;
                    Log.d(TAG, "Attempting copy of block " + String.valueOf(blocks_copied) + " which has " + String.valueOf(length) + " bytes."); //TODO remove if publishing
                    os.write(buffer, 0, length);
                    bytes_copied += length;
                }
                stage++;
                Log.d(TAG,
                        "Finished copying Database " + dbname +
                                " from the assets folder, to  " + f.getPath() +
                                String.valueOf(bytes_copied) + "were copied, in " +
                                String.valueOf(blocks_copied) + " blocks of size " +
                                String.valueOf(buffer_size) + "."
                ); //TODO remove if publishing
                //Close the streams
                os.flush();
                stage++;
                os.close();
                stage++;
                is.close();
                Log.d(TAG, "All Streams have been flushed and closed.");
            } catch (IOException e) {
                String exception_message = "";
                e.printStackTrace();
                switch (stage) {
                    case 0:
                        exception_message = "Error trying to open the asset " + dbname;
                        break;
                    case 1:
                        exception_message = "Error opening Database file for output, path is " + f.getPath();
                        break;
                    case 2:
                        exception_message = "Error flushing written database file " + f.getPath();
                        break;
                    case 3:
                        exception_message = "Error closing written database file " + f.getPath();
                        break;
                    case 4:
                        exception_message = "Error closing asset file " + f.getPath();
    
                }
                throw new RuntimeException("Unable to copy the database from the asset folder." + exception_message + " see starck-trace above.");
            }
        }
    
        /**
         * Copy the databsse from the assets folder where asset name and dbname are the same
         * @param context
         * @param dbname
         * @param deleteExistingDB
         */
        public static void copyDataBase(Context context, String dbname, boolean deleteExistingDB) {
            copyDataBase(context, dbname,dbname,deleteExistingDB);
        }
    
        /**
         * Get the SQLite_user_vesrion from the DB in the asset folder
         *
         * @param context           needed to get the appropriate package assets
         * @param assetfilename     the name of the asset file (assumes/requires name matches database)
         * @return                  the version number as stored in the asset DB
         */
        public static int getVersionFromDBInAssetFolder(Context context, String assetfilename) {
            InputStream is;
            try {
                is = context.getAssets().open(assetfilename);
            } catch (IOException e) {
                return OUCH;
            }
            return getDBVersionFromInputStream(is);
        }
    
        /**
         * Get the version from the database itself without opening the database as an SQliteDatabase
         * @param context   Needed to ascertain package
         * @param dbname    the name of the dataabase
         * @return          the version number extracted
         */
        public static int getVersionFromDBFile(Context context, String dbname) {
            InputStream is;
            try {
                is = new FileInputStream(new File(context.getDatabasePath(dbname).toString()));
            } catch (IOException e) {
                return OUCH;
            }
            return getDBVersionFromInputStream(is);
        }
    
        /**
         * Get the Database Version (user_version) from an inputstream
         *  Note the inputstream is closed
         * @param is    The Inputstream
         * @return      The extracted version number
         */
        private static int getDBVersionFromInputStream(InputStream is) {
            int rv = -1, dbversion_offset = 60, dbversion_length = 4 ;
            byte[] dbfileheader = new byte[64];
            byte[] dbversion = new byte[4];
            try {
                is.read(dbfileheader);
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
                return rv;
            }
    
            for (int i = 0; i < dbversion_length; i++ ) {
                dbversion[i] = dbfileheader[dbversion_offset + i];
            }
            return ByteBuffer.wrap(dbversion).getInt();
        }
    
        /**
         * Check to see if the asset file exists
         *
         * @param context           needed to get the appropriate package
         * @param assetfilename     the name of the asset file to check
         * @return                  true if the asset file exists, else false
         */
        public static boolean ifAssetFileExists(Context context, String assetfilename) {
            try {
                context.getAssets().open(assetfilename);
            } catch (IOException e) {
                return false;
            }
            return true;
        }
    
    
        /**
         * Delete the backup
         * @param context
         * @param dbname
         */
        public static void clearForceBackups(Context context, String dbname) {
            String[] fulllist = new String[tempfiles.length + 1];
    
            for (int i = 0;i < tempfiles.length; i++) {
                fulllist[i] = tempfiles[i];
            }
            fulllist[tempfiles.length] = ""; // Add "" so database file backup is also deleted
            for (String s: fulllist) {
                File tmpf = new File(context.getDatabasePath(dbname + s + backup).toString());
                if (tmpf.exists()) {
                    tmpf.delete();
                }
            }
        }
    }
    
    • メソッド名とコメントで上記のコードを説明できるといいのですが。

    アセットファイルには2つあります:-

    • pev1.db -上記の元の既存のデータベース
    • pev1mod.db -変更されました(追加の列、UNIQUE制約、および追加の行)。

    データベースヘルパー(SQLOpenHelperのサブクラス)、つまり PEV2DBHelper.java 、データベースバージョン( DBVERSION )は制御に使用され、APKのバージョン(DBよりも頻繁に変更される可能性があります)とは異なります

    • onUpgradeを使用しようとしたときに問題が見つかりました したがって、データベースの user_versionを取得する方法です。 SQLiteDatabase経由ではなく、ファイルから。

    これがPEV2DBHelper.javaです :-

    /**
     * MORE COMPLEX EXAMPLE RETAINING USER DATA
     */
    public class PEV2DBHelper extends SQLiteOpenHelper {
    
        public static final String DBNAME = "pev1.db";
        public static final String ASSETTOCOPY_DBV2 = "pev1mod.db"; //<<<<<<<<<< changed DB
        public static final int DBVERSION = 2; //<<<<<<<<<< increase and db file from assets will copied keeping existing data
        Context mContext;
    
        public PEV2DBHelper(Context context) {
            super(context, DBNAME, null, DBVERSION);
    
    
            int dbversion = DBAssetHandler.getVersionFromDBFile(context,DBNAME);
            Log.d("DBFILEVERSION","Database File Version = " + String.valueOf(dbversion));
            int af1version = DBAssetHandler.getVersionFromDBInAssetFolder(context,DBNAME);
            Log.d("DBFILEVERSION","Asset Database File Version = " + String.valueOf(af1version));
            int af2version = DBAssetHandler.getVersionFromDBInAssetFolder(context,ASSETTOCOPY_DBV2);
            Log.d("DBFILEVERSION","Asset Database File Version = " + String.valueOf(af2version));
    
            // cater for different DBVERSIONS (for testing )
            if (!DBAssetHandler.checkDataBase(context,DBNAME)) {
    
                //If new installation of the APP then copy the appropriate asset file for the DB
                switch (DBVERSION) {
                    case 1:
                        DBAssetHandler.copyDataBase(context,DBNAME,DBNAME,false);
                        break;
                    case 2:
                        DBAssetHandler.copyDataBase(context,DBNAME,ASSETTOCOPY_DBV2,false);
                        break;
                }
            }
    
            // If DBVERSION upgraded to 2 with modified DB but wanting to preserve used data
            if (DBAssetHandler.checkDataBase(context,DBNAME) & (DBVERSION > DBAssetHandler.getVersionFromDBFile(context, DBNAME)) & (DBVERSION == 2) ) {
    
                String[] oldcolumns = new String[]{"user","password"};
    
                // Copy in the new DB noting that delete option renames old (truue flag important)
                DBAssetHandler.copyDataBase(context,DBNAME,ASSETTOCOPY_DBV2,true);
    
                //Get the newly copied database
                SQLiteDatabase newdb = SQLiteDatabase.openDatabase(context.getDatabasePath(DBNAME).toString(),null,SQLiteDatabase.OPEN_READWRITE);
                //Get the old database (backup copy)
                SQLiteDatabase olddb =  SQLiteDatabase.openDatabase(context.getDatabasePath(DBNAME + DBAssetHandler.backup).toString(),null,SQLiteDatabase.OPEN_READWRITE);
    
                //Prepare to insert old rows (note user column is UNIQUE so pretty simple scenario just try inserting all and duplicates will be rejected)
                ContentValues cv = new ContentValues();
                Cursor oldcsr = olddb.query("user",null,null,null,null,null,null);
                newdb.beginTransaction();
                while (oldcsr.moveToNext()) {
                    cv.clear();
                    for (String columnname: oldcolumns) {
                        cv.put(columnname,oldcsr.getString(oldcsr.getColumnIndex(columnname)));
                    }
                    newdb.insert("user",null,cv);
                }
                newdb.setTransactionSuccessful();
                newdb.endTransaction();
                newdb.close();
                olddb.close();
    
                // Finally delete the renamed old database
                DBAssetHandler.clearForceBackups(context,DBNAME);
            }
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
    
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    
        }
    }
    
    • 抽出行を追加、削除するメソッドの肥大化はほとんどないことに注意してください。ただし、デモンストレーションを容易にするためにバージョン間の切り替えを処理するという点で、少し複雑すぎます。

    最後に、PEV2DBHelperを利用して、テーブルからログにスキーマと行を書き込むアクティビティの例を示します。

    使用されるアクティビティはMainActivity.java そして:-

    public class MainActivity extends AppCompatActivity {
    
        PEV2DBHelper mDBHlpr2; //DBHelper for example that retains user data
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            doPEV2(); // Test simple more complex scenario
        }
    
    
        private void doPEV2() {
    
            mDBHlpr2 = new PEV2DBHelper(this);
            SQLiteDatabase db = mDBHlpr2.getWritableDatabase();
            Cursor csr = db.query("sqlite_master",null,null,null,null,null,null);
            DatabaseUtils.dumpCursor(csr);
            csr = db.query("user",null,null,null,null,null,null);
            DatabaseUtils.dumpCursor(csr);
            if (PEV2DBHelper.DBVERSION == 1) {
                addUserData(db);
                csr = db.query("user",null,null,null,null,null,null);
                DatabaseUtils.dumpCursor(csr);
            }
            csr.close();
            db.close();
        }
    
        /**
         * Add some user data for testing presevation of that data
         * @param db    the SQLitedatabase
         */
        private void addUserData(SQLiteDatabase db) {
            ContentValues cv = new ContentValues();
            cv.put("user","mr new user");
            cv.put("password","a password");
            db.insert("user",null,cv);
        }
    }
    

    結果

    1。 DBVERSIONを1(新しいアプリ)として最初に実行したとき

    この場合、アセットファイル pev1.db アセットフォルダからコピーされます出力は:-

    2019-02-22 19:07:54.676 28670-28670/? D/DBFILEVERSION: Database File Version = -666666666
    2019-02-22 19:07:54.677 28670-28670/? D/DBFILEVERSION: Asset Database File Version = 0
    2019-02-22 19:07:54.677 28670-28670/? D/DBFILEVERSION: Asset Database File Version = 0
    2019-02-22 19:07:54.677 28670-28670/? D/DBPATH: DB Path is /data/user/0/mjt.so54807516/databases/pev1.db
    2019-02-22 19:07:54.677 28670-28670/? D/COPYDATABASE: Initiated Copy of the database file pev1.db from the assets folder.
    2019-02-22 19:07:54.677 28670-28670/? D/COPYDATABASE: Asset file pev1.db found so attmepting to copy to /data/user/0/mjt.so54807516/databases/pev1.db
    2019-02-22 19:07:54.677 28670-28670/? D/COPYDATABASE: Attempting copy of block 1 which has 4096 bytes.
    2019-02-22 19:07:54.677 28670-28670/? D/COPYDATABASE: Attempting copy of block 2 which has 4096 bytes.
    2019-02-22 19:07:54.677 28670-28670/? D/COPYDATABASE: Finished copying Database pev1.db from the assets folder, to  /data/user/0/mjt.so54807516/databases/pev1.db8192were copied, in 2 blocks of size 4096.
    2019-02-22 19:07:54.678 28670-28670/? D/COPYDATABASE: All Streams have been flushed and closed.
    2019-02-22 19:07:54.678 28670-28670/? D/DBPATH: DB Path is /data/user/0/mjt.so54807516/databases/pev1.db
    2019-02-22 19:07:54.701 28670-28670/? I/System.out: >>>>> Dumping cursor [email protected]
    2019-02-22 19:07:54.701 28670-28670/? I/System.out: 0 {
    2019-02-22 19:07:54.701 28670-28670/? I/System.out:    type=table
    2019-02-22 19:07:54.701 28670-28670/? I/System.out:    name=user
    2019-02-22 19:07:54.701 28670-28670/? I/System.out:    tbl_name=user
    2019-02-22 19:07:54.702 28670-28670/? I/System.out:    rootpage=2
    2019-02-22 19:07:54.702 28670-28670/? I/System.out:    sql=CREATE TABLE "user" (
    2019-02-22 19:07:54.702 28670-28670/? I/System.out:   "_id" INTEGER NOT NULL,
    2019-02-22 19:07:54.702 28670-28670/? I/System.out:   "user" TEXT,
    2019-02-22 19:07:54.702 28670-28670/? I/System.out:   "password" TEXT,
    2019-02-22 19:07:54.702 28670-28670/? I/System.out:   PRIMARY KEY ("_id")
    2019-02-22 19:07:54.702 28670-28670/? I/System.out: )
    2019-02-22 19:07:54.702 28670-28670/? I/System.out: }
    2019-02-22 19:07:54.702 28670-28670/? I/System.out: 1 {
    2019-02-22 19:07:54.702 28670-28670/? I/System.out:    type=table
    2019-02-22 19:07:54.702 28670-28670/? I/System.out:    name=android_metadata
    2019-02-22 19:07:54.702 28670-28670/? I/System.out:    tbl_name=android_metadata
    2019-02-22 19:07:54.702 28670-28670/? I/System.out:    rootpage=3
    2019-02-22 19:07:54.702 28670-28670/? I/System.out:    sql=CREATE TABLE android_metadata (locale TEXT)
    2019-02-22 19:07:54.702 28670-28670/? I/System.out: }
    2019-02-22 19:07:54.702 28670-28670/? I/System.out: <<<<<
    2019-02-22 19:07:54.703 28670-28670/? I/System.out: >>>>> Dumping cursor [email protected]
    2019-02-22 19:07:54.703 28670-28670/? I/System.out: 0 {
    2019-02-22 19:07:54.703 28670-28670/? I/System.out:    _id=1
    2019-02-22 19:07:54.703 28670-28670/? I/System.out:    user=Fred
    2019-02-22 19:07:54.703 28670-28670/? I/System.out:    password=fredpassword
    2019-02-22 19:07:54.703 28670-28670/? I/System.out: }
    2019-02-22 19:07:54.703 28670-28670/? I/System.out: 1 {
    2019-02-22 19:07:54.703 28670-28670/? I/System.out:    _id=2
    2019-02-22 19:07:54.703 28670-28670/? I/System.out:    user=Mary
    2019-02-22 19:07:54.704 28670-28670/? I/System.out:    password=marypassword
    2019-02-22 19:07:54.704 28670-28670/? I/System.out: }
    2019-02-22 19:07:54.704 28670-28670/? I/System.out: <<<<<
    2019-02-22 19:07:54.705 28670-28670/? I/System.out: >>>>> Dumping cursor [email protected]
    2019-02-22 19:07:54.705 28670-28670/? I/System.out: 0 {
    2019-02-22 19:07:54.705 28670-28670/? I/System.out:    _id=1
    2019-02-22 19:07:54.705 28670-28670/? I/System.out:    user=Fred
    2019-02-22 19:07:54.705 28670-28670/? I/System.out:    password=fredpassword
    2019-02-22 19:07:54.706 28670-28670/? I/System.out: }
    2019-02-22 19:07:54.706 28670-28670/? I/System.out: 1 {
    2019-02-22 19:07:54.706 28670-28670/? I/System.out:    _id=2
    2019-02-22 19:07:54.706 28670-28670/? I/System.out:    user=Mary
    2019-02-22 19:07:54.706 28670-28670/? I/System.out:    password=marypassword
    2019-02-22 19:07:54.706 28670-28670/? I/System.out: }
    2019-02-22 19:07:54.706 28670-28670/? I/System.out: 2 {
    2019-02-22 19:07:54.706 28670-28670/? I/System.out:    _id=3
    2019-02-22 19:07:54.706 28670-28670/? I/System.out:    user=mr new user
    2019-02-22 19:07:54.706 28670-28670/? I/System.out:    password=a password
    2019-02-22 19:07:54.706 28670-28670/? I/System.out: }
    2019-02-22 19:07:54.706 28670-28670/? I/System.out: <<<<<
    
    • -666666666はファイルが存在しないためのバージョンであるため、ファイルからバージョンを取得しようとすると、バージョンを取得できなかったことを示すデフォルト値が返されました。

    2。 2回目の実行は、バージョン番号が1であることを除いて、すべて同じです。

    2019-02-22 19:09:43.724 28730-28730/mjt.so54807516 D/DBFILEVERSION: Database File Version = 1
    2019-02-22 19:09:43.724 28730-28730/mjt.so54807516 D/DBFILEVERSION: Asset Database File Version = 0
    2019-02-22 19:09:43.724 28730-28730/mjt.so54807516 D/DBFILEVERSION: Asset Database File Version = 0
    2019-02-22 19:09:43.725 28730-28730/mjt.so54807516 D/DBPATH: DB Path is /data/user/0/mjt.so54807516/databases/pev1.db
    2019-02-22 19:09:43.725 28730-28730/mjt.so54807516 D/DBPATH: DB Path is /data/user/0/mjt.so54807516/databases/pev1.db
    2019-02-22 19:09:43.729 28730-28730/mjt.so54807516 I/System.out: >>>>> 
    ..... etc
    

    3。 DBVERSIONを2に変更した後の次の実行

    2019-02-22 19:13:49.157 28866-28866/mjt.so54807516 D/DBFILEVERSION: Database File Version = 1
    2019-02-22 19:13:49.158 28866-28866/mjt.so54807516 D/DBFILEVERSION: Asset Database File Version = 0
    2019-02-22 19:13:49.158 28866-28866/mjt.so54807516 D/DBFILEVERSION: Asset Database File Version = 0
    2019-02-22 19:13:49.158 28866-28866/mjt.so54807516 D/DBPATH: DB Path is /data/user/0/mjt.so54807516/databases/pev1.db
    2019-02-22 19:13:49.158 28866-28866/mjt.so54807516 D/DBPATH: DB Path is /data/user/0/mjt.so54807516/databases/pev1.db
    2019-02-22 19:13:49.158 28866-28866/mjt.so54807516 D/COPYDATABASE: Initiated Copy of the database file pev1mod.db from the assets folder.
    2019-02-22 19:13:49.159 28866-28866/mjt.so54807516 D/COPYDATABASE: Asset file pev1mod.db found so attmepting to copy to /data/user/0/mjt.so54807516/databases/pev1.db
    2019-02-22 19:13:49.159 28866-28866/mjt.so54807516 D/COPYDATABASE: Attempting copy of block 1 which has 4096 bytes.
    2019-02-22 19:13:49.159 28866-28866/mjt.so54807516 D/COPYDATABASE: Attempting copy of block 2 which has 4096 bytes.
    2019-02-22 19:13:49.159 28866-28866/mjt.so54807516 D/COPYDATABASE: Attempting copy of block 3 which has 4096 bytes.
    2019-02-22 19:13:49.159 28866-28866/mjt.so54807516 D/COPYDATABASE: Attempting copy of block 4 which has 4096 bytes.
    2019-02-22 19:13:49.159 28866-28866/mjt.so54807516 D/COPYDATABASE: Finished copying Database pev1.db from the assets folder, to  /data/user/0/mjt.so54807516/databases/pev1.db16384were copied, in 4 blocks of size 4096.
    2019-02-22 19:13:49.159 28866-28866/mjt.so54807516 D/COPYDATABASE: All Streams have been flushed and closed.
    2019-02-22 19:13:49.186 28866-28866/mjt.so54807516 E/SQLiteDatabase: Error inserting password=fredpassword user=Fred
        android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: user.user (code 2067 SQLITE_CONSTRAINT_UNIQUE)
            at 
        .........
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
    2019-02-22 19:13:49.191 28866-28866/mjt.so54807516 E/SQLiteDatabase: Error inserting password=a password user=mr new user
        android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: user.user (code 2067 SQLITE_CONSTRAINT_UNIQUE)
            at 
       .............
    2019-02-22 19:13:49.209 28866-28866/mjt.so54807516 I/System.out: >>>>> Dumping cursor [email protected]
    2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out: 0 {
    2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:    type=table
    2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:    name=user
    2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:    tbl_name=user
    2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:    rootpage=2
    2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:    sql=CREATE TABLE "user" (
    2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:   "_id" INTEGER NOT NULL,
    2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:   "user" TEXT,
    2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:   "password" TEXT,
    2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:   "email" TEXT,
    2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:   PRIMARY KEY ("_id"),
    2019-02-22 19:13:49.210 28866-28866/mjt.so54807516 I/System.out:   CONSTRAINT "user" UNIQUE ("user")
    2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out: )
    2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out: }
    2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out: 1 {
    2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    type=index
    2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    name=sqlite_autoindex_user_1
    2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    tbl_name=user
    2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    rootpage=4
    2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    sql=null
    2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out: }
    2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out: 2 {
    2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    type=table
    2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    name=android_metadata
    2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    tbl_name=android_metadata
    2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    rootpage=3
    2019-02-22 19:13:49.211 28866-28866/mjt.so54807516 I/System.out:    sql=CREATE TABLE android_metadata (locale TEXT)
    2019-02-22 19:13:49.212 28866-28866/mjt.so54807516 I/System.out: }
    2019-02-22 19:13:49.212 28866-28866/mjt.so54807516 I/System.out: <<<<<
    2019-02-22 19:13:49.212 28866-28866/mjt.so54807516 I/System.out: >>>>> Dumping cursor [email protected]
    2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out: 0 {
    2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out:    _id=1
    2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out:    user=Fred
    2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out:    password=fredpassword
    2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out:    [email protected]
    2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out: }
    2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out: 1 {
    2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out:    _id=2
    2019-02-22 19:13:49.213 28866-28866/mjt.so54807516 I/System.out:    
    ...... etc
    


    1. sqlplus現在接続されているデータベースセッションの詳細を見つける方法

    2. 透過的なデータ暗号化と常に暗号化

    3. パターンに一致する値を含むオブジェクトのJSON配列を検索します

    4. SQLでの制約の確認