capacitor-community / sqlite

⚡Capacitor plugin for native & electron SQLite databases.
MIT License
495 stars 118 forks source link

isJsonValid is on android false and on iOS true #102

Closed bennyvenassi closed 3 years ago

bennyvenassi commented 3 years ago

Hi, I switched from sqlite 2.4 to 2.9.14 and I'm trying to implement it in ionic for iOS and Android. I'm facing the issue, that my code works fine on iOS. On android I'm getting the result "false" from CapacitorSQLite.isJsonValid function and the firsttime database setup isn't performed.

I organized the database interactions in a seperate service file.

The code for the firsttime setup from my service:

export class StorageService {
  dbReady = new BehaviorSubject(false);
  dbName = "";
  sqlite: SQLiteConnection;
  db: SQLiteDBConnection;
  isService: boolean = false;
  platform: string;

  constructor(private http: HttpClient) {}

initializePlugin(): Promise<boolean> {
    console.log('starting sqlite initialization...');
    return new Promise((resolve) => {
      this.platform = Capacitor.platform;
      console.log("*** platform " + this.platform);
      const sqlitePlugin: any = CapacitorSQLite;
      this.sqlite = new SQLiteConnection(sqlitePlugin);
      this.isService = true;
      console.log("$$$ in service this.isService " + this.isService + " $$$");
      this.setupDatabase();
      resolve(true);
    });
  }
  /**
   * Echo a value
   * @param value
   */
  async echo(value: string): Promise<capEchoResult> {
    console.log("&&&& in echo this.sqlite " + this.sqlite + " &&&&");
    if (this.sqlite != null) {
      return await this.sqlite.echo(value);
    } else {
      return null;
    }
  }

  private async setupDatabase() {
    console.log('setting up database');
    const dbSetupDone = await Storage.get({ key: DB_SETUP_KEY });
    if (!dbSetupDone.value) {
      this.downloadDatabase();
    } else {
      this.dbName = (await Storage.get({ key: DB_NAME_KEY })).value;
      let db = await this.createConnection();
      if (db) {
        this.dbReady.next(true);
      }
    }
  }

  private async createConnection(): Promise<SQLiteDBConnection | null> {
    console.log('setting up db connection');
    if (this.sqlite != null) {
      this.db = await this.sqlite.createConnection(
        this.dbName,
        false,
        "no-encryption",
        1
      );
      console.log(this.db);
      if (this.db != null) {
        this.db.open();
        return this.db;
      } else {
        return null;
      }
    } else {
      return null;
    }
  }

  private downloadDatabase(update = false) {
    console.log('downloading database');
    this.http
      .get("../assets/start_db.json")
      .subscribe(async (jsonExport: JsonSQLite) => {
        const jsonstring = JSON.stringify(jsonExport);
        let isValid = false;
        try {
          const isValidTry = await CapacitorSQLite.isJsonValid({ jsonstring });
          console.log(isValidTry)
          isValid = isValidTry.result;
        } catch (e) {
          console.error(e);
        }
        console.log(isValid);
        if (isValid) {
          console.log('dbjson is valid');
          this.dbName = jsonExport.database;
          try {
            await Storage.set({ key: DB_NAME_KEY, value: this.dbName });
            await this.sqlite.importFromJson(jsonstring);
            await Storage.set({ key: DB_SETUP_KEY, value: "1" });
          } catch (e) {
            console.error(e);
          }
          console.log('db set');
          // Your potential logic to detect offline changes later
          let db = await this.createConnection();
          if (db) {
            this.dbReady.next(true);
            if (!update) {
              await db.createSyncTable();
            } else {
              await db.setSyncDate("" + new Date().getTime());
            }
          }
        }
      });
  }

and my database json:

{ "database": "doublenote-db", "version": 1, "encrypted": false, "mode": "full", "tables": [ { "name": "notes", "schema": [ { "column": "id", "value": "INTEGER PRIMARY KEY NOT NULL" }, { "column": "title", "value": "TEXT NOT NULL" }, { "column": "note", "value": "TEXT NOT NULL" }, { "column": "color", "value": "TEXT NOT NULL" }, { "column": "created", "value": "INTEGER DEFAULT (strftime('%s', 'now'))" }, { "column": "last_modified", "value": "INTEGER DEFAULT (strftime('%s', 'now'))" } ], "values": [ ] }, { "name": "trash", "schema": [ { "column": "id", "value": "INTEGER PRIMARY KEY NOT NULL" }, { "column": "title", "value": "TEXT NOT NULL" }, { "column": "note", "value": "TEXT NOT NULL" }, { "column": "color", "value": "TEXT NOT NULL" }, { "column": "created", "value": "INTEGER DEFAULT (strftime('%s', 'now'))" }, { "column": "last_modified", "value": "INTEGER DEFAULT (strftime('%s', 'now'))" } ], "values": [ ] }, { "name": "archive", "schema": [ { "column": "id", "value": "INTEGER PRIMARY KEY NOT NULL" }, { "column": "title", "value": "TEXT NOT NULL" }, { "column": "note", "value": "TEXT NOT NULL" }, { "column": "color", "value": "TEXT NOT NULL" }, { "column": "created", "value": "INTEGER DEFAULT (strftime('%s', 'now'))" }, { "column": "last_modified", "value": "INTEGER DEFAULT (strftime('%s', 'now'))" } ], "values": [ ] } ] }

thank you in advance!

jepiqueau commented 3 years ago

@bennyvenassi I tested it in my code and the json object works fine on Android. To replicate your code i will need to have the full code of the storage service with the import i don't know where Storage is defined

bennyvenassi commented 3 years ago

okay, no problem:

import { Injectable } from "@angular/core";
import { Plugins, Capacitor } from "@capacitor/core";
import "@capacitor-community/sqlite";
import { HttpClient } from "@angular/common/http";
import { BehaviorSubject } from "rxjs";

import {
  JsonSQLite,
  SQLiteConnection,
  SQLiteDBConnection,
  capEchoResult,
} from "@capacitor-community/sqlite";
import { Note } from "./note.model";
const { CapacitorSQLite, Storage } = Plugins;

const DB_SETUP_KEY = "first_db_setup";
const DB_NAME_KEY = "db_name";

@Injectable({
  providedIn: "root",
})
export class StorageService {
  dbReady = new BehaviorSubject(false);
  dbName = "";
  sqlite: SQLiteConnection;
  db: SQLiteDBConnection;
  isService: boolean = false;
  platform: string;

  constructor(private http: HttpClient) {}

  /**
   * Plugin Initialization
   */
  initializePlugin(): Promise<boolean> {
    console.log('starting sqlite initialization...');
    return new Promise((resolve) => {
      this.platform = Capacitor.platform;
      console.log("*** platform " + this.platform);
      const sqlitePlugin: any = CapacitorSQLite;
      this.sqlite = new SQLiteConnection(sqlitePlugin);
      this.isService = true;
      console.log("$$$ in service this.isService " + this.isService + " $$$");
      this.setupDatabase();
      resolve(true);
    });
  }
  /**
   * Echo a value
   * @param value
   */
  async echo(value: string): Promise<capEchoResult> {
    console.log("&&&& in echo this.sqlite " + this.sqlite + " &&&&");
    if (this.sqlite != null) {
      return await this.sqlite.echo(value);
    } else {
      return null;
    }
  }

  private async setupDatabase() {
    console.log('setting up database');
    const dbSetupDone = await Storage.get({ key: DB_SETUP_KEY });
    if (!dbSetupDone.value) {
      this.downloadDatabase();
    } else {
      this.dbName = (await Storage.get({ key: DB_NAME_KEY })).value;
      let db = await this.createConnection();
      if (db) {
        this.dbReady.next(true);
      }
    }
  }

  private async createConnection(): Promise<SQLiteDBConnection | null> {
    console.log('setting up db connection');
    if (this.sqlite != null) {
      this.db = await this.sqlite.createConnection(
        this.dbName,
        false,
        "no-encryption",
        1
      );
      console.log(this.db);
      if (this.db != null) {
        this.db.open();
        return this.db;
      } else {
        return null;
      }
    } else {
      return null;
    }
  }

  private downloadDatabase(update = false) {
    console.log('downloading database');
    this.http
      .get("../assets/start_db.json")
      .subscribe(async (jsonExport: JsonSQLite) => {
        const jsonstring = JSON.stringify(jsonExport);
        let isValid = false;
        try {
          const isValidTry = await CapacitorSQLite.isJsonValid({ jsonstring });
          console.log(isValidTry)
          isValid = isValidTry.result;
        } catch (e) {
          console.error(e);
        }
        console.log(isValid);
        if (isValid) {
          console.log('dbjson is valid');
          this.dbName = jsonExport.database;
          try {
            await Storage.set({ key: DB_NAME_KEY, value: this.dbName });
            await this.sqlite.importFromJson(jsonstring);
            await Storage.set({ key: DB_SETUP_KEY, value: "1" });
          } catch (e) {
            console.error(e);
          }
          console.log('db set');
          // Your potential logic to detect offline changes later
          let db = await this.createConnection();
          if (db) {
            this.dbReady.next(true);
            if (!update) {
              await db.createSyncTable();
            } else {
              await db.setSyncDate("" + new Date().getTime());
            }
          }
        }
      });
  }

}
jepiqueau commented 3 years ago

@bennyvenassi i ran it without the storage

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import {HttpClient} from "@angular/common/http";

import { Plugins, Capacitor } from '@capacitor/core';
import '@capacitor-community/sqlite';
import { SQLiteDBConnection, SQLiteConnection, capSQLiteSet,
         capSQLiteChanges, capEchoResult, capSQLiteResult, 
         capSQLiteValues, JsonSQLite} from '@capacitor-community/sqlite';
const { CapacitorSQLite } = Plugins;
const DB_NAME_KEY = "db-name";
const DB_SETUP_KEY = "db-setup";

@Injectable({
    providedIn: 'root',
})

export class StorageService {
    dbReady = new BehaviorSubject(false);
    dbName = "";
    sqlite: SQLiteConnection;
    db: SQLiteDBConnection;
    isService: boolean = false;
    platform: string;
//    storage: Storage = new Storage();

    constructor(private http: HttpClient) {}

  initializePlugin(): Promise<boolean> {
      console.log('starting sqlite initialization...');
      return new Promise((resolve) => {
        this.platform = Capacitor.platform;
        console.log("*** platform " + this.platform);
        const sqlitePlugin: any = CapacitorSQLite;
        this.sqlite = new SQLiteConnection(sqlitePlugin);
        this.isService = true;
        console.log("$$$ in service this.isService " + this.isService + " $$$");
        this.setupDatabase();
        resolve(true);
      });
    }
    /**
     * Echo a value
     * @param value
     */
    async echo(value: string): Promise<capEchoResult> {
      console.log("&&&& in echo this.sqlite " + this.sqlite + " &&&&");
      if (this.sqlite != null) {
        return await this.sqlite.echo(value);
      } else {
        return null;
      }
    }

    private async setupDatabase() {
      console.log('setting up database');
 //     const dbSetupDone = await this.storage.get({ key: DB_SETUP_KEY });
 //     if (!dbSetupDone.value) {
        this.downloadDatabase();
/*      } else {
        this.dbName = (await this.storage.get({ key: DB_NAME_KEY })).value;
        let db = await this.createConnection();
            if (db) {
            this.dbReady.next(true);
            }
        }
     */
    }

    private async createConnection(): Promise<SQLiteDBConnection | null> {
      console.log('setting up db connection');
      if (this.sqlite != null) {
        this.db = await this.sqlite.createConnection(
          this.dbName,
          false,
          "no-encryption",
          1
        );
        console.log(this.db);
        if (this.db != null) {
          this.db.open();
          return this.db;
        } else {
          return null;
        }
      } else {
        return null;
      }
    }

    private downloadDatabase(update = false) {
      console.log('downloading database');
      this.http
        .get("../../assets/start_db.json")
        .subscribe(async (jsonExport: JsonSQLite) => {
          const jsonstring = JSON.stringify(jsonExport);
          let isValid = false;
          try {
            const isValidTry = await CapacitorSQLite.isJsonValid({ jsonstring });
            console.log(isValidTry)
            isValid = isValidTry.result;
          } catch (e) {
            console.error(e);
          }
          console.log(isValid);
          if (isValid) {
            console.log('dbjson is valid');
            this.dbName = jsonExport.database;
            try {
//              await this.storage.set({ key: DB_NAME_KEY, value: this.dbName });
              await this.sqlite.importFromJson(jsonstring);
//              await this.storage.set({ key: DB_SETUP_KEY, value: "1" });
            } catch (e) {
              console.error(e);
            }
            console.log('db set');
            // Your potential logic to detect offline changes later
            let db = await this.createConnection();
            if (db) {
              this.dbReady.next(true);
              if (!update) {
                await db.createSyncTable();
              } else {
                await db.setSyncDate("" + new Date().getTime());
              }
            }
          }
        });
    }
}

and call it like this in a Page

    const ret = await this._store.initializePlugin();
    console.log(">>>> in App  Store this.initPlugin " + JSON.stringify(ret))
      let result: any = await this._store.echo("Hello World from Store");
      console.log(" from Echo " + result.value);
      if( result.value === "Hello World from Store" ) {
        return true;
      } else {
        return false;
      }

and it works fine

Did you run a npx cap sync before the 'build` when you have move from one version to the other

jepiqueau commented 3 years ago

@bennyvenassi i do not think the Storage is responsible for. so i do not know what to do. i put this

.get("../../assets/start_db.json")

as in my app the service storage.services.ts is under app/services folder

jepiqueau commented 3 years ago

@bennyvenassi I add the Storage plugin and removing the commented lines and it works fine

jepiqueau commented 3 years ago

@bennyvenassi In the service that you provided you never close the connection. so i assume that it is something that you start at the launching of your app and after the connection and the database stayed open. So i also assume that after this you are using another service to interface the database and make query, insert ...

bennyvenassi commented 3 years ago

Thank you for your answers! I'm using only the storeage.service.ts to interact with the database. The storage service is supposed to insert the json object as the first start is performed. But it stops at the point validating the json object. Therefore I've included so many console.logs.

E.g. the consolelogs from android studio:

2021-04-04 13:17:14.871 8489-8584/io.vulper.doublenote V/Capacitor: callback: 92374607, pluginId: Device, methodName: getInfo, methodData: {}
2021-04-04 13:17:14.879 8489-8584/io.vulper.doublenote V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 92374608, pluginId: Storage, methodName: get
2021-04-04 13:17:14.879 8489-8584/io.vulper.doublenote V/Capacitor: callback: 92374608, pluginId: Storage, methodName: get, methodData: {"key":"db-setup"}
2021-04-04 13:17:14.906 8489-8543/io.vulper.doublenote D/Capacitor: Handling local request: http://localhost/assets/icon/favicon.png
2021-04-04 13:17:15.390 8489-8489/io.vulper.doublenote I/Capacitor/Console: File: http://localhost/main-es2015.js - Line 81 - Msg: starting sqlite initialization...
2021-04-04 13:17:15.391 8489-8489/io.vulper.doublenote I/Capacitor/Console: File: http://localhost/main-es2015.js - Line 84 - Msg: *** platform android
2021-04-04 13:17:15.393 8489-8489/io.vulper.doublenote I/Capacitor/Console: File: http://localhost/main-es2015.js - Line 88 - Msg: $$$ in service this.isService true $$$
2021-04-04 13:17:15.396 8489-8489/io.vulper.doublenote I/Capacitor/Console: File: http://localhost/main-es2015.js - Line 110 - Msg: setting up database
2021-04-04 13:17:15.399 8489-8489/io.vulper.doublenote I/Capacitor/Console: File: http://localhost/main-es2015.js - Line 351 - Msg: >>>> in App  this.initPlugin true
2021-04-04 13:17:15.406 8489-8489/io.vulper.doublenote I/Choreographer: Skipped 32 frames!  The application may be doing too much work on its main thread.
2021-04-04 13:17:16.125 8489-8584/io.vulper.doublenote V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 92374609, pluginId: Device, methodName: getLanguageCode
2021-04-04 13:17:16.126 8489-8584/io.vulper.doublenote V/Capacitor: callback: 92374609, pluginId: Device, methodName: getLanguageCode, methodData: {}
2021-04-04 13:17:16.135 8489-8543/io.vulper.doublenote D/Capacitor: Handling local request: http://localhost/assets/start_db.json
2021-04-04 13:17:16.162 8489-8489/io.vulper.doublenote I/Capacitor/Console: File: http://localhost/main-es2015.js - Line 144 - Msg: downloading database
2021-04-04 13:17:16.237 8489-8543/io.vulper.doublenote D/Capacitor: Handling local request: http://localhost/assets/i18n/de.json
2021-04-04 13:17:16.277 8489-8489/io.vulper.doublenote I/Capacitor/Console: File: http://localhost/vendor-es2015.js - Line 284 - Msg: isJsonValid [object Object]
2021-04-04 13:17:16.278 8489-8489/io.vulper.doublenote I/Capacitor/Console: File: http://localhost/main-es2015.js - Line 158 - Msg: false

and for comparison from xcode, where the code works fine:

WebView loaded
⚡️  [log] - Angular is running in development mode. Call enableProdMode() to enable production mode.
⚡️  [log] - Ionic Native: deviceready event fired after 319 ms
⚡️  To Native ->  App addListener 18384560
⚡️  To Native ->  Device getInfo 18384561
⚡️  To Native ->  Storage get 18384562
⚡️  [log] - starting sqlite initialization...
⚡️  [log] - *** platform ios
⚡️  [log] - $$$ in service this.isService true $$$
⚡️  [log] - setting up database
⚡️  TO JS {"value":null}
⚡️  [log] - >>>> in App  this.initPlugin true
⚡️  To Native ->  Device getLanguageCode 18384563
⚡️  TO JS {"value":"de"}
⚡️  [log] - downloading database
⚡️  To Native ->  CapacitorSQLite isJsonValid 18384564
⚡️  TO JS {"result":true}
⚡️  [log] - {"result":true}
⚡️  [log] - true
⚡️  [log] - dbjson is valid
⚡️  To Native ->  Storage set 18384565
⚡️  TO JS {}
⚡️  To Native ->  CapacitorSQLite importFromJson 18384566
databaseName: doublenote-dbSQLite.db 
after dropTables retChanges: 0
after dropIndexes retChanges: 0
after dropTriggers retChanges: 0
⚡️  TO JS {"changes":{"changes":0}}
⚡️  To Native ->  Storage set 18384567
⚡️  TO JS {}
⚡️  [log] - db set
⚡️  [log] - setting up db connection
⚡️  To Native ->  CapacitorSQLite createConnection 18384568
databaseName: doublenote-dbSQLite.db 
⚡️  TO JS {"result":true}
⚡️  [log] - >>> in SQLiteDBConnection dbName doublenote-db
⚡️  [log] - {"dbName":"doublenote-db","sqlite":{}}

I also tried to delete the node_modules and android folder and reinstall the packages, unfortunately that doesnt solve the problem.

What do you mean on which point I should close the connection?

I want my code to the followings steps:

  1. Check if their is already a db (therefore the db_setup in the local sorage)
  2. if there is already a db: connect to the db with the name saved in storage
  3. if there is not a db yet, insert the json from the assets folder and then connect to the db
  4. and the dbready beheaviour subject shall inform components (e.g. my overview page), when its safe to query tables, after the dbReady Subject is true. so there are no queries while the storage service is setting up the database.

On android dbReady never changes to true, because of the functions at the point, that the json is not valid.

jepiqueau commented 3 years ago

@bennyvenassi I do not understand in the android log the following

 2021-04-04 13:17:16.237 8489-8543/io.vulper.doublenote D/Capacitor: Handling local request: http://localhost/assets/i18n/de.json

what is that file ?

jepiqueau commented 3 years ago

@bennyvenassi look t https://github.com/jepiqueau/angular-sqlite-app-starter/tree/refactor testjson102. For me it is working fine.

bennyvenassi commented 3 years ago

@bennyvenassi I do not understand in the android log the following

 2021-04-04 13:17:16.237 8489-8543/io.vulper.doublenote D/Capacitor: Handling local request: http://localhost/assets/i18n/de.json

what is that file ?

that log is not from sqlite, i'm using a internationalization plugin and this is the log of the device language.

I'll try tomorrow some more testing...

jepiqueau commented 3 years ago

@bennyvenassi Ok look at my code clone it and see if it works it should

bennyvenassi commented 3 years ago

@jepiqueau now i'm really clueless. I just copied theexample repo and it's not working either.

It makes no sense to me, I deleted the node_modules and android folder serveral times now, somehow android won't work.

Android console logs of the example repo:

2021-04-05 12:38:04.548 6414-6414/com.jeep.app.ionic.angular I/Capacitor/Console: File: http://localhost/testimportjson-testimportjson-module-es2015.js - Line 142 - Msg: %%%% in TestimportjsonPage this._sqlite [object Object]
2021-04-05 12:38:04.550 6414-6414/com.jeep.app.ionic.angular I/Capacitor/Console: File: http://localhost/main-es2015.js - Line 262 - Msg: &&&& in echo this.sqlite [object Object] &&&&
2021-04-05 12:38:04.551 6414-6414/com.jeep.app.ionic.angular I/Capacitor/Console: File: http://localhost/vendor-es2015.js - Line 161 - Msg: ECHO in Web plugin [object Object]
2021-04-05 12:38:04.552 6414-6414/com.jeep.app.ionic.angular I/Capacitor/Console: File: http://localhost/testimportjson-testimportjson-module-es2015.js - Line 160 - Msg:  from Echo Hello World
2021-04-05 12:38:04.553 6414-6414/com.jeep.app.ionic.angular I/Capacitor/Console: File: http://localhost/vendor-es2015.js - Line 284 - Msg: isJsonValid [object Object]
2021-04-05 12:38:04.553 6414-6414/com.jeep.app.ionic.angular I/Capacitor/Console: File: http://localhost/testimportjson-testimportjson-module-es2015.js - Line 168 - Msg: isJsonValid: Not implemented on Web Platform
2021-04-05 12:38:04.554 6414-6414/com.jeep.app.ionic.angular I/Capacitor/Console: File: http://localhost/testimportjson-testimportjson-module-es2015.js - Line 153 - Msg: $$$ runTest failed

and the working xcode logs:

[log] - *** platform ios
⚡️  [log] - $$$ in service this.isService true $$$
⚡️  [log] - >>>> in App  this.initPlugin true
⚡️  [log] - **** ionViewWillEnter false
⚡️  [log] - %%%% in TestimportjsonPage this._sqlite [object Object]
⚡️  [log] - &&&& in echo this.sqlite [object Object] &&&&
⚡️  To Native ->  CapacitorSQLite echo 37460068
⚡️  TO JS {"value":"Hello World"}
⚡️  [log] -  from Echo Hello World
⚡️  To Native ->  CapacitorSQLite isJsonValid 37460069
⚡️  TO JS {"result":true}
⚡️  [log] - $$$ dataToImport Json Object is valid $$$
⚡️  To Native ->  CapacitorSQLite importFromJson 37460070
databaseName: db-from-jsonSQLite.db 
database path /Users/benny/Library/Developer/CoreSimulator/Devices/****/data/Containers/Data/Application/****/Documents/db-from-jsonSQLite.db
after dropTables retChanges: 0
after dropIndexes retChanges: 0
after dropTriggers retChanges: 0
⚡️  TO JS {"changes":{"changes":8}}
⚡️  [log] - full import result 8
jepiqueau commented 3 years ago

@bennyvenassi It seems that you did not update your MainActivity.java file. This is still required for capacitor/core@2.4.7. This will not be required in the 3.0.0

package com.jeep.app.ionic.angular;

import android.os.Bundle;

import com.getcapacitor.BridgeActivity;
import com.getcapacitor.Plugin;
import com.getcapacitor.community.database.sqlite.CapacitorSQLite;

import java.util.ArrayList;

public class MainActivity extends BridgeActivity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Initializes the Bridge
    this.init(savedInstanceState, new ArrayList<Class<? extends Plugin>>() {{
      // Additional plugins you've installed go here
      // Ex: add(TotallyAwesomePlugin.class);
      add(CapacitorSQLite.class);
    }});
  }
}

i saw this int the following log

2021-04-05 12:38:04.553 6414-6414/com.jeep.app.ionic.angular I/Capacitor/Console: File: http://localhost/testimportjson-testimportjson-module-es2015.js - Line 168 - Msg: isJsonValid: Not implemented on Web Platform

Having declare the plugin it should work

bennyvenassi commented 3 years ago

Sorry, yes, you are awesome. while changing the plugin version and deleting the android folder I have totally overseen this. Thank you a lot for your help!!