Problem:
We try to run your update script in h2 for testing.
With every version we have a update.sql to run the updates on the DB.
But we get a problem when trying to delete a constraint (foreign key):
Error:
Exception in thread "main" org.h2.jdbc.JdbcSQLException: Index "FP_ACL_PERMISSION_UNQ" belongs to constraint "CONSTRAINT_31B"; SQL statement:
ALTER TABLE FP_ACL_PERMISSION DROP INDEX FP_ACL_PERMISSION_UNQ; [90085-191]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
at org.h2.message.DbException.get(DbException.java:179)
at org.h2.command.ddl.DropIndex.update(DropIndex.java:63)
at org.h2.command.CommandContainer.update(CommandContainer.java:98)
at org.h2.command.Command.executeUpdate(Command.java:258)
at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:184)
at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:158)
at com.rbs.mib.portal.HelloWorld.main(HelloWorld.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Code to reproduce the problem:
public class ReproduceH2Problem{
public static void main(String... args) throws Exception {
Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:~/test;MODE=ORACLE", "sa", "sa");
Statement stat = conn.createStatement();
stat.execute("DROP ALL OBJECTS");
// init v1.0.0
stat.execute("CREATE TABLE FP_ACL" +
"(" +
" ACL_ID INTEGER NOT NULL," +
" REPORT_KEY VARCHAR2(100) NOT NULL," +
" PARENT_ACL_ID INTEGER" +
");");
stat.execute("CREATE TABLE FP_ACL_PERMISSION" +
"(" +
" PERMISSION_ID INTEGER NOT NULL," +
" ACL_ID INTEGER NOT NULL," +
" NAME VARCHAR2(100)" +
");");
stat.execute("CREATE UNIQUE INDEX FP_ACL_PERMISSION_UNQ ON FP_ACL_PERMISSION" +
"(ACL_ID, NAME);");
stat.execute("ALTER TABLE FP_ACL_PERMISSION ADD " +
"CHECK (ACL_ID IS NOT NULL);");
stat.execute("ALTER TABLE FP_ACL_PERMISSION ADD " +
"PRIMARY KEY " +
"(PERMISSION_ID);");
stat.execute("ALTER TABLE FP_ACL_PERMISSION ADD " +
"CONSTRAINT FP_ACL_PERMISSION_UNQ " +
"UNIQUE (ACL_ID, NAME);");
// !! this seems to be the "bad guy" !!
stat.execute("ALTER TABLE FP_ACL_PERMISSION ADD " +
"FOREIGN KEY (ACL_ID) " +
"REFERENCES FP_ACL (ACL_ID);");
//update v1.0.1
stat.execute("ALTER TABLE FP_ACL_PERMISSION DROP CONSTRAINT FP_ACL_PERMISSION_UNQ;");
//here the problems start
stat.execute("ALTER TABLE FP_ACL_PERMISSION DROP INDEX FP_ACL_PERMISSION_UNQ;");
stat.execute("ALTER TABLE FP_ACL_PERMISSION DROP COLUMN ACL_ID;");
stat.execute("ALTER TABLE FP_ACL_PERMISSION ADD CONSTRAINT FP_ACL_PERMISSION_UNQ UNIQUE (NAME);");
stat.close();
conn.close();
}
}
I had to do a workaround by adding meta commands in the sql scripts as
cannot rename the constraints (h2 does not support)
direct subselect does not work
alter table TABLE_NAME drop constraint
(select unique_index_name
from information_schema.constraints
where table_name='TABLE_NAME' and CONSTRAINT_TYPE='REFERENTIAL' and COLUMN_LIST= ='SHORT_ID')
Workaround:
Meta Command in update.sql script:
--H2-DROP-REFERENCE TABLE::FP_ACL_PERMISSION:: COLUMN::ACL_ID::
ALTER TABLE FP_ACL_PERMISSION DROP INDEX FP_ACL_PERMISSION_UNQ;
Filter meta command in groovy test:
public static enum H2_META_COMMAND {
DROP_REFERENCE("--H2-DROP-REFERENCE")
private String command;
H2_META_COMMAND(String command){
this.command = command
}
String getCommand(){
return command;
}
}
and
/**
* This checks for custom H2 Meta commands to close oracle compatibility gap
* #param sqlCommand a single command
*/
public static void executeH2MetaCommands(String sqlSingleCommand, Statement statement){
if(sqlCommand.contains(H2_META_COMMAND.DROP_REFERENCE.getCommand())){
int tableStart = sqlCommand.indexOf("TABLE::", sqlCommand.indexOf(H2_META_COMMAND.DROP_REFERENCE.getCommand()) + H2_META_COMMAND.DROP_REFERENCE.getCommand().size() ) + 7
String table = sqlCommand.substring(tableStart, sqlCommand.indexOf("::",tableStart))
int colStart = sqlCommand.indexOf("COLUMN::") + 8
String col = sqlCommand.substring(colStart, sqlCommand.indexOf("::",colStart))
ResultSet rs = statement.executeQuery("select CONSTRAINT_NAME from information_schema.constraints where table_name='"+ table+"' and CONSTRAINT_TYPE='REFERENTIAL' and COLUMN_LIST= '"+col+"'")
rs.next()
String constraintName = rs.getString(1)
rs.close()
statement.execute("ALTER TABLE FP_ACL_PERMISSION DROP constraint " + constraintName)
}
}
Related
I am using the Flink table API to pull data from a kinesis topic into a table. I want to periodically pull that data into a temporary table and run a custom scalar function on it. However, I notice that my scalar function is not being called at all.
Here is the code for the Kinesis table :
this.tableEnv.executeSql("CREATE TABLE transactions (\n" +
" entry STRING,\n" +
" sequence_number VARCHAR(128) NOT NULL METADATA FROM 'sequence-number' VIRTUAL,\n" +
" shard_id VARCHAR(128) NOT NULL METADATA FROM 'shard-id' VIRTUAL,\n" +
" arrival_time TIMESTAMP(3) METADATA FROM 'timestamp' VIRTUAL,\n" +
" WATERMARK FOR arrival_time AS arrival_time - INTERVAL '5' SECOND\n" +
") WITH (\n" +
" 'connector' = 'kinesis',\n" +
" 'stream' = '" + streamName + "',\n" +
" 'aws.region' = 'us-west-2', \n" +
" 'format' = 'raw'\n" +
")");
Then, I want to periodically call a tumble every second which pulls data from kinesis and updates a temporary table.
My temporary table is defined like this:
this.tableEnv.executeSql("CREATE TABLE temporaryTable (\n" +
" entry STRING,\n" +
" sequence_number VARCHAR(128) NOT NULL,\n" +
" shard_id VARCHAR(128) NOT NULL,\n" +
" arrival_time TIMESTAMP(3),\n" +
" record_list STRING NOT NULL,\n" +
" PRIMARY KEY (shard_id, sequence_number) NOT ENFORCED" +
") WITH (\n" +
" 'connector' = 'print'\n" +
")");
I then have a code to do the tumbling :
Table inMemoryTable = transactions.
window(Tumble.over(lit(1).second()).on($("arrival_time")).as("log_ts"))
.groupBy($("entry"), $("sequence_number"), $("log_ts"), $("shard_id"), $("arrival_time"))
.select(
$("entry"),
$("sequence_number"), $("shard_id"), $("arrival_time"),
(call(CustomFunction.class, $("entry")).as("record_list")));
inMemoryTable.executeInsert(temporaryTable)
The CustomFunction class looks like this :
public class CustomFunction extends ScalarFunction {
#DataTypeHint("STRING")
public String eval(
#DataTypeHint("STRING") String serializedEntry) throws IOException {
return "asd";
}
When I run this code in Flink, I dont get anything in the stdout so obviously I am missing something.
Here is the Flink UI:
Image as link as I dont have enough rep
Thanks for any help.
I am able to get the stream to print with:
driver.tableEnv.getConfig().getConfiguration().setString("table.exec.source.idle", "10000 ms");
driver.env.getConfig().setAutoWatermarkInterval(5000);
I am trying to set a task to refresh a materialized view every hour. I have tried this:
I ran and INSERT of new data to the original table. The Materialized View updated instantly
Forcing the table to drop, the undrop the table that makes up the materialized view. It resulted in a full restoration at the specific time - though this would get expensive quickly
Drop table BookInventory;
Undrop table BookInventory;
I could not find anything in documentation on scheduling a creation of a materilaized view. Has anyone done this before?
Another alternative is that you simply create your own "materialized view" via a custom procedure that you can schedule via a task.
the procedure creates a temp table like the current, including grants. Then inserts the data into this table from a view. Finally swap the tables and drop the temp table. Best to create this as a Transient table since there's no need for Time Travel.
CREATE OR REPLACE PROCEDURE utl.arch_create_mview_sp(P_TABLE_NM VARCHAR, P_VIEW_NM VARCHAR)
RETURNS STRING
LANGUAGE JAVASCRIPT
EXECUTE AS CALLER
AS $$
var result = "";
var sqlCmd = "";
var rs = "";
var tmpTableNM = P_TABLE_NM + "_tmp";
try {
sqlCmd = "CREATE OR REPLACE TABLE " + tmpTableNM + " LIKE " + P_TABLE_NM + " COPY GRANTS";
snowflake.execute( {sqlText: sqlCmd} );
sqlCmd = "INSERT INTO " + tmpTableNM + " SELECT * FROM " + P_VIEW_NM;
rs = snowflake.execute( {sqlText: sqlCmd} );
rs.next();
result = "rows inserted: " + rs.getColumnValue(1);
sqlCmd = "ALTER TABLE " + P_TABLE_NM + " SWAP WITH " + tmpTableNM;
snowflake.execute( {sqlText: sqlCmd} );
sqlCmd = "DROP TABLE " + tmpTableNM;
snowflake.execute( {sqlText: sqlCmd} );
}
catch (err) {
result = "Failed: Code: " + err.code + " | State: " + err.state;
result += "\n Message: " + err.message;
result += "\nStack Trace:\n" + err.stackTraceTxt;
}
}
return result;
$$;
You can suspend and resume materialized views. But you cannot query a suspended MV. What are you trying to accomplish? You are not going to save money, only defer the cost.
I am a newbie at programming so please bear with me (I am taking an online course).
I am at a point in the lesson where we were supposed to create a database in a java class and a table called "pets" would get created in the onCreate method of the main activity (called CatalogActivity).
At this point, I have already downloaded the app exactly in the form it is supposed to be (to make sure I haven't made a mistake before in my code), but when I run the app on the emulator, no table gets created.
The idea is, when I run the app, the database called shelter.db should get created and in it, the table called "pets". This table is defined in the PetDbHelper.java class.
When searching in Android Studio terminal, I see no tables in shelter.db. So I downloaded shelter.db to my PC and opened it with SQL browser - still no "pets" table. Looks like for some reason, the table doesn't get created :(
I don't know why as I seem to be following all the instructions to the point.
Does anyone have advice how to fix this, please?
Here is a link to the code: https://github.com/soralka/PetsApp_error
Thanks in advance!
Soralka
Here's the LOG when I use the CommonSQLiteUtilities class:
07-18 19:37:44.344 15867-15867/? I/le.android.pet: Not late-enabling -Xcheck:jni (already on)
07-18 19:37:44.416 15867-15867/? W/le.android.pet: Unexpected CPU variant for X86 using defaults: x86
07-18 19:37:44.662 15867-15867/com.example.android.pets I/le.android.pet: The ClassLoaderContext is a special shared library.
07-18 19:37:45.027 15867-15867/com.example.android.pets W/le.android.pet: JIT profile information will not be recorded: profile file does not exits.
07-18 19:37:45.051 15867-15867/com.example.android.pets I/chatty: uid=10083(com.example.android.pets) identical 10 lines
07-18 19:37:45.051 15867-15867/com.example.android.pets W/le.android.pet: JIT profile information will not be recorded: profile file does not exits.
07-18 19:37:45.137 15867-15867/com.example.android.pets I/InstantRun: starting instant run server: is main process
07-18 19:37:45.840 15867-15867/com.example.android.pets W/le.android.pet: Accessing hidden method Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (light greylist, reflection)
07-18 19:37:45.850 15867-15867/com.example.android.pets W/le.android.pet: Accessing hidden method Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V (light greylist, reflection)
07-18 19:37:46.066 15867-15867/com.example.android.pets D/SQLITE_CSU: DatabaseList Row 1 Name=main File=/data/user/0/com.example.android.pets/databases/shelter.db
07-18 19:37:46.068 15867-15867/com.example.android.pets D/SQLITE_CSU: Database Version = 1
07-18 19:37:46.071 15867-15867/com.example.android.pets D/SQLITE_CSU: Table Name = android_metadata Created Using = CREATE TABLE android_metadata (locale TEXT)
07-18 19:37:46.073 15867-15867/com.example.android.pets D/SQLITE_CSU: Table = android_metadata ColumnName = locale ColumnType = TEXT Default Value = null PRIMARY KEY SEQUENCE = 0
07-18 19:37:46.074 15867-15867/com.example.android.pets D/SQLITE_CSU: Table Name = pets Created Using = CREATE TABLE pets (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, breed TEXT, gender INTEGER NOT NULL, weight INTEGER NOT NULL DEFAULT 0)
07-18 19:37:46.077 15867-15867/com.example.android.pets D/SQLITE_CSU: Table = pets ColumnName = _id ColumnType = INTEGER Default Value = null PRIMARY KEY SEQUENCE = 1
07-18 19:37:46.078 15867-15867/com.example.android.pets D/SQLITE_CSU: Table = pets ColumnName = name ColumnType = TEXT Default Value = null PRIMARY KEY SEQUENCE = 0
07-18 19:37:46.079 15867-15867/com.example.android.pets D/SQLITE_CSU: Table = pets ColumnName = breed ColumnType = TEXT Default Value = null PRIMARY KEY SEQUENCE = 0
Table = pets ColumnName = gender ColumnType = INTEGER Default Value = null PRIMARY KEY SEQUENCE = 0
Table = pets ColumnName = weight ColumnType = INTEGER Default Value = 0 PRIMARY KEY SEQUENCE = 0
07-18 19:37:46.080 15867-15867/com.example.android.pets D/SQLITE_CSU: Table Name = sqlite_sequence Created Using = CREATE TABLE sqlite_sequence(name,seq)
07-18 19:37:46.082 15867-15867/com.example.android.pets D/SQLITE_CSU: Table = sqlite_sequence ColumnName = name ColumnType = Default Value = null PRIMARY KEY SEQUENCE = 0
Table = sqlite_sequence ColumnName = seq ColumnType = Default Value = null PRIMARY KEY SEQUENCE = 0
07-18 19:37:46.134 15867-15867/com.example.android.pets D/OpenGLRenderer: Skia GL Pipeline
07-18 19:37:46.260 15867-15886/com.example.android.pets I/ConfigStore: android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasWideColorDisplay retrieved: 0
07-18 19:37:46.262 15867-15886/com.example.android.pets I/ConfigStore: android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasHDRDisplay retrieved: 0
07-18 19:37:46.262 15867-15886/com.example.android.pets I/OpenGLRenderer: Initialized EGL, version 1.4
07-18 19:37:46.262 15867-15886/com.example.android.pets D/OpenGLRenderer: Swap behavior 1
07-18 19:37:46.262 15867-15886/com.example.android.pets W/OpenGLRenderer: Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...
07-18 19:37:46.263 15867-15886/com.example.android.pets D/OpenGLRenderer: Swap behavior 0
07-18 19:37:46.284 15867-15886/com.example.android.pets D/EGL_emulation: eglCreateContext: 0xe4fbebc0: maj 3 min 0 rcv 3
07-18 19:37:46.347 15867-15886/com.example.android.pets D/EGL_emulation: eglMakeCurrent: 0xe4fbebc0: ver 3 0 (tinfo 0xe79c7930)
07-18 19:37:46.534 15867-15886/com.example.android.pets D/EGL_emulation: eglMakeCurrent: 0xe4fbebc0: ver 3 0 (tinfo 0xe79c7930)
Based upon your code I can see no issues.
You may wish to try deleting the App's data, or uninstalling the App adding the line below (or adding the CommonSQLiteUtilities class and adding the two lines that have been added for testing below) and then rerunning the App (this would overcome any issue you may have had that resulted in the database being created but not any tables, in which case the onCreate method would not run so any corrective changes (if any) may not have been applied).
Amending the CatalogActivity by adding the line :-
displayDatabaseInfo();
Results in the EditText being updated as expected (shows 0 rows in pets database).
If the underlying table didn't exist then if you added the line above, you'd experience an exception along the lines of :-
07-17 01:28:51.001 1321-1321/pets.pets E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{pets.pets/pets.pets.CatalogActivity}: android.database.sqlite.SQLiteException: no such table: notatable (code 1): , while compiling: SELECT * FROM notatable
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2059)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
at android.app.ActivityThread.access$600(ActivityThread.java:130)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
Caused by: android.database.sqlite.SQLiteException: no such table: notatable (code 1): , while compiling: SELECT * FROM notatable
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:882)
at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:493)
at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)
at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:44)
at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1314)
at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1253)
at pets.pets.CatalogActivity.displayDatabaseInfo(CatalogActivity.java:40)
at pets.pets.CatalogActivity.onCreate(CatalogActivity.java:22)
at android.app.Activity.performCreate(Activity.java:5008)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
at android.app.ActivityThread.access$600(ActivityThread.java:130)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
Note forced by changing the table name to notatable i.e.
Cursor cursor = db.rawQuery("SELECT * FROM " + "notatable", null);
Adding the CommonSQLiteUtilities class from Are there any methods that assist with resolving common SQLite issues?
and then adding the line :-
CommonSQLiteUtilities.logDatabaseInfo(mDbHelper.getWritableDatabase());
results in the log containing :-
07-17 01:36:51.191 1399-1399/? D/SQLITE_CSU: DatabaseList Row 1 Name=main File=/data/data/pets.pets/databases/shelter.db
Database Version = 1
Table Name = android_metadata Created Using = CREATE TABLE android_metadata (locale TEXT)
Table = android_metadata ColumnName = locale ColumnType = TEXT Default Value = null PRIMARY KEY SEQUENCE = 0
Table Name = pets Created Using = CREATE TABLE pets (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, breed TEXT, gender INTEGER NOT NULL, weight INTEGER NOT NULL DEFAULT 0)
Table = pets ColumnName = _id ColumnType = INTEGER Default Value = null PRIMARY KEY SEQUENCE = 1
Table = pets ColumnName = name ColumnType = TEXT Default Value = null PRIMARY KEY SEQUENCE = 0
Table = pets ColumnName = breed ColumnType = TEXT Default Value = null PRIMARY KEY SEQUENCE = 0
Table = pets ColumnName = gender ColumnType = INTEGER Default Value = null PRIMARY KEY SEQUENCE = 0
Table = pets ColumnName = weight ColumnType = INTEGER Default Value = 0 PRIMARY KEY SEQUENCE = 0
Table Name = sqlite_sequence Created Using = CREATE TABLE sqlite_sequence(name,seq)
Table = sqlite_sequence ColumnName = name ColumnType = Default Value = null PRIMARY KEY SEQUENCE = 0
Table = sqlite_sequence ColumnName = seq ColumnType = Default Value = null PRIMARY KEY SEQUENCE = 0
Further confirming that the table is created.
The testing above was done using the following code :-
CatalogActivity.java
public class CatalogActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_catalog);
//displayDatabaseInfo();
PetDbHelper mDbHelper = new PetDbHelper(this);
SQLiteDatabase db = mDbHelper.getReadableDatabase();
displayDatabaseInfo();
CommonSQLiteUtilities.logDatabaseInfo(mDbHelper.getWritableDatabase());
}
/**
* Temporary helper method to display information in the onscreen TextView about the state of
* the pets database.
*/
private void displayDatabaseInfo() {
// To access our database, we instantiate our subclass of SQLiteOpenHelper
// and pass the context, which is the current activity.
PetDbHelper mDbHelper = new PetDbHelper(this);
// Create and/or open a database to read from it
SQLiteDatabase db = mDbHelper.getReadableDatabase();
// Perform this raw SQL query "SELECT * FROM pets"
// to get a Cursor that contains all rows from the pets table.
Cursor cursor = db.rawQuery("SELECT * FROM " + PetContract.PetEntry.TABLE_NAME, null);
try {
// Display the number of rows in the Cursor (which reflects the number of rows in the
// pets table in the database).
TextView displayView = (TextView) findViewById(R.id.text_view_pet);
displayView.setText("Number of rows in pets database table: " + cursor.getCount());
} finally {
// Always close the cursor when you're done reading from it. This releases all its
// resources and makes it invalid.
cursor.close();
}
}
}
PetDbHelper.java
public class PetDbHelper extends SQLiteOpenHelper {
public static final String LOG_TAG = PetDbHelper.class.getSimpleName();
/**
* Name of the database file
*/
private static final String DATABASE_NAME = "shelter.db";
/**
* Database version. If you change the database schema, you must increment the database version.
*/
private static final int DATABASE_VERSION = 1;
/**
* Constructs a new instance of {#link PetDbHelper}.
*
* #param context of the app
*/
public PetDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
/**
* This is called when the database is created for the first time.
*/
#Override
public void onCreate(SQLiteDatabase db) {
// Create a String that contains the SQL statement to create the pets table
String SQL_CREATE_PETS_TABLE = "CREATE TABLE " + PetContract.PetEntry.TABLE_NAME + " ("
+ PetContract.PetEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ PetContract.PetEntry.COLUMN_PET_NAME + " TEXT NOT NULL, "
+ PetContract.PetEntry.COLUMN_PET_BREED + " TEXT, "
+ PetContract.PetEntry.COLUMN_PET_GENDER + " INTEGER NOT NULL, "
+ PetContract.PetEntry.COLUMN_PET_WEIGHT + " INTEGER NOT NULL DEFAULT 0);";
// Execute the SQL statement
db.execSQL(SQL_CREATE_PETS_TABLE);
}
/**
* This is called when the database needs to be upgraded.
*/
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// The database is still at version 1, so there's nothing to do be done here.
}
}
PetContract.java
public final class PetContract {
private PetContract() {}
public static final class PetEntry implements BaseColumns {
public static final String TABLE_NAME = "pets";
public static final String _ID = BaseColumns._ID;
public static final String COLUMN_PET_NAME = "name";
public static final String COLUMN_PET_BREED = "breed";
public static final String COLUMN_PET_GENDER = "gender";
public static final String COLUMN_PET_WEIGHT = "weight";
/* Possible values for gender. */
public static final int GENDER_UNKNOWN = 0;
public static final int GENDER_MALE = 1;
public static final int GENDER_FEMALE = 2;
}
}
CommonSQLiteUtilities.java
public class CommonSQLiteUtilities {
public static final boolean ERROR_CHECKING_ON = true;
public static final boolean ERROR_CHECKING_OFF = false;
// SQLite MASTER TABLE definitions
static final String SQLITE_MASTER = "sqlite_master";
static final String SM_TABLE_TYPE_COLUMN = "type";
static final String SM_NAME_COLUMN = "name";
static final String SM_TABLENAME_COLUMN = "tbl_name";
static final String SM_ROOTPAGE_COLUMN = "rootpage";
static final String SM_SQL_COLUMN = "sql";
static final String SM_TYPE_TABLE = "table";
static final String SM_TYPE_INDEX = "index";
static final String PRAGMA_STATEMENT = "PRAGMA ";
static final String PRAGMA_DATABASELIST = "database_list";
static final String PRAGMA_USERVERSION = "user_version";
static final String PRAGMA_ENCODING = "encoding";
static final String PRAGMA_FOREIGNKEYLIST = "foreign_key_list";
static final String PRAGMA_INDEXINFO = "index_info";
static final String PRAGMA_INDEXLIST = "index_list";
static final String PRAGMA_TABLEINFO = "table_info";
static final String PRAGMA_DBLIST_SEQ_COL = "seq";
static final String PRAGMA_DBLIST_NAME_COL = "name";
static final String PRAGMA_DBLIST_FILE_COL = "file";
static final String PRAGMA_TABLEINFO_CID_COL = "cid";
static final String PRAGMA_TABLEINFO_NAME_COl = "name";
static final String PRAGMA_TABLEINFO_TYPE_COL = "type";
static final String PRAGMA_TABLEINFO_NOTNULL_COL = "notnull";
static final String PRAGMA_TABLEINFO_DEFAULTVALUE_COL = "dflt_value";
static final String PRAGMA_TABLEINFO_PRIMARYKEY_COL = "pk";
static final String CSU_TAG = "SQLITE_CSU";
private CommonSQLiteUtilities() {}
/**
* Write Database information to the log;
* Information wrttien is:
* the database path, (will/should show connected databases)
* the version number (note! user version i.e. version coded in DBHelper),
* the tables in the database (includes android_metadata but not sqlite_master),
* the columns of the tables
* #param db The SQLite database to be interrogated
*/
public static void logDatabaseInfo(SQLiteDatabase db) {
// Issue PRAGMA database_list commnand
Cursor dblcsr = db.rawQuery(PRAGMA_STATEMENT + PRAGMA_DATABASELIST,null);
// Write databases to the log
while (dblcsr.moveToNext()) {
Log.d(CSU_TAG,"DatabaseList Row " + Integer.toString(dblcsr.getPosition() + 1) +
" Name=" + dblcsr.getString(dblcsr.getColumnIndex(PRAGMA_DBLIST_NAME_COL)) +
" File=" + dblcsr.getString(dblcsr.getColumnIndex(PRAGMA_DBLIST_FILE_COL))
);
}
dblcsr.close();
// Issue PRAGMA user_version to get the version and write to the log
//Note! to set user_version use execSQL not rawQuery
Cursor uvcsr = db.rawQuery(PRAGMA_STATEMENT + PRAGMA_USERVERSION,null);
while (uvcsr.moveToNext()) {
Log.d(CSU_TAG,"Database Version = " +
Integer.toString(uvcsr.getInt(uvcsr.getColumnIndex(PRAGMA_USERVERSION))));
}
uvcsr.close();
// Select all table entry rows from sqlite_master
Cursor tlcsr = db.rawQuery("SELECT * FROM " +
SQLITE_MASTER + " WHERE " +
SM_TABLE_TYPE_COLUMN + "='" + SM_TYPE_TABLE + "'"
,null);
// For each table write table information to the log
// (inner loop gets column info per table)
while (tlcsr.moveToNext()) {
String current_table = tlcsr.getString(tlcsr.getColumnIndex(SM_TABLENAME_COLUMN));
Log.d(CSU_TAG,
"Table Name = " + current_table +
" Created Using = " + tlcsr.getString(tlcsr.getColumnIndex(SM_SQL_COLUMN)),
null
);
// Issue PRAGMA tabel_info for the current table
Cursor ticsr = db.rawQuery(PRAGMA_STATEMENT + PRAGMA_TABLEINFO +
"(" + current_table + ")",
null
);
// Write column info (see headings below) to the log
while (ticsr.moveToNext()) {
Log.d(CSU_TAG,"Table = " +
current_table +
" ColumnName = " +
ticsr.getString(ticsr.getColumnIndex(PRAGMA_TABLEINFO_NAME_COl)) +
" ColumnType = " +
ticsr.getString(ticsr.getColumnIndex(PRAGMA_TABLEINFO_TYPE_COL)) +
" Default Value = " +
ticsr.getString(ticsr.getColumnIndex(PRAGMA_TABLEINFO_DEFAULTVALUE_COL)) +
" PRIMARY KEY SEQUENCE = " + Integer.toString(
ticsr.getInt(ticsr.getColumnIndex(PRAGMA_TABLEINFO_PRIMARYKEY_COL))
)
);
}
ticsr.close();
}
tlcsr.close();
}
/**
* Generic get all rows from an SQlite table,
* allowing the existence of the table to be checked and also
* allowing the ROWID to be added AS a supplied string
*
* #param db The SQLiteDatabase
* #param tablename The name of the table from which the
* returned cursor will be created from;
* Note!
* #param use_error_checking Whether ot not to try to detect errors
* currently just table doesn't exist,
* true to turn on, false to turn off
* ERROR_CHECKING_ON = true
* ERROR_CHECKING_OFF = false
* #param forceRowidAs If length of string passed is 1 or greater
* then a column, as an alias of ROWID, will be
* added to the cursor
* #return the extracted cursor, or in the case of the
* underlying table not existing an empty cursor
* with no columns
*/
public static Cursor getAllRowsFromTable(SQLiteDatabase db,
String tablename,
boolean use_error_checking,
String forceRowidAs) {
String[] columns = null;
// Tablename must be at least 1 character in length
if (tablename.length() < 1) {
Log.d(CSU_TAG,new Object(){}.getClass().getEnclosingMethod().getName() +
" is finishing as the provided tablename is less than 1 character in length"
);
return new MatrixCursor(new String[]{});
}
// If use_error_checking is true then check that the table exists
// in the sqlite_master table
if (use_error_checking) {
Cursor chkcsr = db.query(SQLITE_MASTER,null,
SM_TABLE_TYPE_COLUMN + "=? AND "
+ SM_TABLENAME_COLUMN + "=?",
new String[]{SM_TYPE_TABLE,tablename},
null,null,null
);
// Ooops table is not in the Database so return an empty
// column-less cursor
if (chkcsr.getCount() < 1) {
Log.d(CSU_TAG,"Table " + tablename +
" was not located in the SQLite Database Master Table."
);
// return empty cursor with no columns
return new MatrixCursor(new String[]{});
}
chkcsr.close();
}
// If forcing an alias of ROWID then user ROWID AS ???, *
if(forceRowidAs != null && forceRowidAs.length() > 0) {
columns = new String[]{"rowid AS " +forceRowidAs,"*"};
}
// Finally return the Cursor but trap any exceptions
try {
return db.query(tablename, columns, null, null, null, null, null);
} catch (Exception e) {
Log.d(CSU_TAG,"Exception encountered but trapped when querying table " + tablename +
" Message was: \n" + e.getMessage());
Log.d(CSU_TAG,"Stacktrace was:");
e.printStackTrace();
return new MatrixCursor(new String[]{});
}
}
/**
* Create and return a Cursor devoid of any rows and columns
* Not used, prehaps of very little use.
* #param db The Sqlite database in which the cursor is to be created
* #return The empty Cursor
*/
private static Cursor getEmptyColumnLessCursor(SQLiteDatabase db) {
return new MatrixCursor(new String[]{});
}
/**
* Write column names in the passed Cursor to the log
* #param csr The Cursor to be inspected.
*/
public static void logCursorColumns(Cursor csr) {
Log.d(CSU_TAG,
new Object(){}.getClass().getEnclosingMethod().getName() +
" invoked. Cursor has the following " +
Integer.toString(csr.getColumnCount())+
" columns.");
int position = 0;
for (String column: csr.getColumnNames()) {
position++;
Log.d(CSU_TAG,"Column Name " +
Integer.toString(position) +
" is "
+ column
);
}
}
/**
* Write the contents of the Cursor to the log
* #param csr The Cursor that is to be displayed in the log
*/
public static void logCursorData(Cursor csr) {
int columncount = csr.getColumnCount();
int rowcount = csr.getCount();
int csrpos = csr.getPosition(); //<<< added 20171016
Log.d(CSU_TAG,
new Object(){}.getClass().getEnclosingMethod().getName() +
" Cursor has " +
Integer.toString(rowcount) +
" rows with " +
Integer.toString(columncount) + " columns."
);
csr.moveToPosition(-1); //Ensure that all rows are retrieved <<< added 20171016
while (csr.moveToNext()) {
String unobtainable = "unobtainable!";
String logstr = "Information for row " + Integer.toString(csr.getPosition() + 1) + " offset=" + Integer.toString(csr.getPosition());
for (int i=0; i < columncount;i++) {
logstr = logstr + "\n\tFor Column " + csr.getColumnName(i);
switch (csr.getType(i)) {
case Cursor.FIELD_TYPE_NULL:
logstr = logstr + " Type is NULL";
break;
case Cursor.FIELD_TYPE_FLOAT:
logstr = logstr + "Type is FLOAT";
break;
case Cursor.FIELD_TYPE_INTEGER:
logstr = logstr + " Type is INTEGER";
break;
case Cursor.FIELD_TYPE_STRING:
logstr = logstr + " Type is STRING";
break;
case Cursor.FIELD_TYPE_BLOB:
logstr = logstr + " Type is BLOB";
break;
}
String strval_log = " value as String is ";
String lngval_log = " value as long is ";
String dblval_log = " value as double is ";
String blbval_log = "";
try {
strval_log = strval_log + csr.getString(i);
lngval_log = lngval_log + csr.getLong(i);
dblval_log = dblval_log + csr.getDouble(i);
} catch (Exception e) {
strval_log = strval_log + unobtainable;
lngval_log = lngval_log + unobtainable;
dblval_log = dblval_log + unobtainable;
try {
blbval_log = " value as blob is " +
getBytedata(csr.getBlob(i),24);
} catch (Exception e2) {
e2.printStackTrace();
}
}
logstr = logstr + strval_log + lngval_log + dblval_log + blbval_log;
}
Log.d(CSU_TAG,logstr);
}
csr.moveToPosition(csrpos); // restore cursor position <<< added 20171016
}
/**
* Return a hex string of the given byte array
* #param bytes The byte array to be converted to a hexadecimal string
* #param limit the maximum number of bytes;
* note returned string will be up to twice as long
* #return The byte array represented as a hexadecimal string
*/
private static String getBytedata(byte[] bytes, int limit) {
if (bytes.length < limit) {
return convertBytesToHex(bytes);
} else {
byte[] subset = new byte[limit];
System.arraycopy(bytes,0,subset,0,limit);
return convertBytesToHex(subset);
}
}
// HEX characters as a char array for use by convertBytesToHex
private final static char[] hexarray = "0123456789ABCDEF".toCharArray();
/**
* Return a hexadecimal string representation of the passed byte array
* #param bytes The byte array to be represented.
* #return The string representing the byte array as hexadecimal
*/
private static String convertBytesToHex(byte[] bytes) {
char[] hexstr = new char[bytes.length * 2];
for (int i=0; i < bytes.length; i++) {
int h = bytes[i] & 0xFF;
hexstr[i * 2] = hexarray[h >>> 4];
hexstr[i * 2 + 1] = hexarray[h & 0xF];
}
return new String(hexstr);
}
}
activity_catalog.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CatalogActivity">
<TextView
android:id="#+id/text_view_pet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
I found out what the main issue was: I was using an emulator with an API 28. Apparently, anything above API 23 is an issue when trying to access databases.
So I made a new emulator API 23 and now I don't have any issues with the permissions etc.
#MikeT - this seems to have been why I was struggling with getting the permissions.
I inherited a project and I'm running into a SQL error that I'm not sure how to fix.
On an eCommerce site, the code is inserting order shipping info into another database table.
Here's the code that is inserting the info into the table:
string sql = "INSERT INTO AC_Shipping_Addresses
(pk_OrderID, FullName, Company, Address1, Address2, City, Province, PostalCode, CountryCode, Phone, Email, ShipMethod, Charge_Freight, Charge_Subtotal)
VALUES (" + _Order.OrderNumber;
sql += ", '" + _Order.Shipments[0].ShipToFullName.Replace("'", "''") + "'";
if (_Order.Shipments[0].ShipToCompany == "")
{
sql += ", '" + _Order.Shipments[0].ShipToFullName.Replace("'", "''") + "'";
}
else
{
sql += ", '" + _Order.Shipments[0].ShipToCompany.Replace("'", "''") + "'";
}
sql += ", '" + _Order.Shipments[0].Address.Address1.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.Address2.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.City.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.Province.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.PostalCode.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.Country.Name.Replace("'", "''") + "'";
sql += ", '" + _Order.Shipments[0].Address.Phone.Replace("'", "''") + "'";
if (_Order.Shipments[0].ShipToEmail == "")
{
sql += ",'" + _Order.BillToEmail.Replace("'", "''") + "'";
}
else
{
sql += ",'" + _Order.Shipments[0].ShipToEmail.Replace("'", "''") + "'";
}
sql += ", '" + _Order.Shipments[0].ShipMethod.Name.Replace("'", "''") + "'";
sql += ", " + shippingAmount;
sql += ", " + _Order.ProductSubtotal.ToString() + ")";
bll.dbUpdate(sql);
It is working correctly, but it is also outputting the following SQL error:
Violation of PRIMARY KEY constraint 'PK_AC_Shipping_Addresses'. Cannot insert
duplicate key in object 'dbo.AC_Shipping_Addresses'. The duplicate key value
is (165863).
From reading similar questions, it seems that I should declare the ID in the statement.
Is that correct? How would I adjust the code to fix this issue?
I was getting the same error on a restored database when I tried to insert a new record using the EntityFramework. It turned out that the Indentity/Seed was screwing things up.
Using a reseed command fixed it.
DBCC CHECKIDENT ('[Prices]', RESEED, 4747030);GO
I'm pretty sure pk_OrderID is the PK of AC_Shipping_Addresses
And you are trying to insert a duplicate via the _Order.OrderNumber?
Do a
select * from AC_Shipping_Addresses where pk_OrderID = 165863;
or select count(*) ....
Pretty sure you will get a row returned.
It is telling you that you are already using pk_OrderID = 165863 and cannot have another row with that value.
if you want to not insert if there is a row
insert into table (pk, value)
select 11 as pk, 'val' as value
where not exists (select 1 from table where pk = 11)
What is the value you're passing to the primary key (presumably "pk_OrderID")? You can set it up to auto increment, and then there should never be a problem with duplicating the value - the DB will take care of that. If you need to specify a value yourself, you'll need to write code to determine what the max value for that field is, and then increment that.
If you have a column named "ID" or such that is not shown in the query, that's fine as long as it is set up to autoincrement - but it's probably not, or you shouldn't get that err msg. Also, you would be better off writing an easier-on-the-eye query and using params. As the lad of nine years hence inferred, you're leaving your database open to SQL injection attacks if you simply plop in user-entered values. For example, you could have a method like this:
internal static int GetItemIDForUnitAndItemCode(string qry, string unit, string itemCode)
{
int itemId;
using (SqlConnection sqlConn = new SqlConnection(ReportRunnerConstsAndUtils.CPSConnStr))
{
using (SqlCommand cmd = new SqlCommand(qry, sqlConn))
{
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add("#Unit", SqlDbType.VarChar, 25).Value = unit;
cmd.Parameters.Add("#ItemCode", SqlDbType.VarChar, 25).Value = itemCode;
sqlConn.Open();
itemId = Convert.ToInt32(cmd.ExecuteScalar());
}
}
return itemId;
}
...that is called like so:
int itemId = SQLDBHelper.GetItemIDForUnitAndItemCode(GetItemIDForUnitAndItemCodeQuery, _unit, itemCode);
You don't have to, but I store the query separately:
public static readonly String GetItemIDForUnitAndItemCodeQuery = "SELECT PoisonToe FROM Platypi WHERE Unit = #Unit AND ItemCode = #ItemCode";
You can verify that you're not about to insert an already-existing value by (pseudocode):
bool alreadyExists = IDAlreadyExists(query, value) > 0;
The query is something like "SELECT COUNT FROM TABLE WHERE BLA = #CANDIDATEIDVAL" and the value is the ID you're potentially about to insert:
if (alreadyExists) // keep inc'ing and checking until false, then use that id value
Justin wants to know if this will work:
string exists = "SELECT 1 from AC_Shipping_Addresses where pk_OrderID = " _Order.OrderNumber; if (exists > 0)...
What seems would work to me is:
string existsQuery = string.format("SELECT 1 from AC_Shipping_Addresses where pk_OrderID = {0}", _Order.OrderNumber);
// Or, better yet:
string existsQuery = "SELECT COUNT(*) from AC_Shipping_Addresses where pk_OrderID = #OrderNumber";
// Now run that query after applying a value to the OrderNumber query param (use code similar to that above); then, if the result is > 0, there is such a record.
To prevent inserting a record that exist already. I'd check if the ID value exists in the database. For the example of a Table created with an IDENTITY PRIMARY KEY:
CREATE TABLE [dbo].[Persons] (
ID INT IDENTITY(1,1) PRIMARY KEY,
LastName VARCHAR(40) NOT NULL,
FirstName VARCHAR(40)
);
When JANE DOE and JOE BROWN already exist in the database.
SET IDENTITY_INSERT [dbo].[Persons] OFF;
INSERT INTO [dbo].[Persons] (FirstName,LastName)
VALUES ('JANE','DOE');
INSERT INTO Persons (FirstName,LastName)
VALUES ('JOE','BROWN');
DATABASE OUTPUT of TABLE [dbo].[Persons] will be:
ID LastName FirstName
1 DOE Jane
2 BROWN JOE
I'd check if i should update an existing record or insert a new one. As the following JAVA example:
int NewID = 1;
boolean IdAlreadyExist = false;
// Using SQL database connection
// STEP 1: Set property
System.setProperty("java.net.preferIPv4Stack", "true");
// STEP 2: Register JDBC driver
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
// STEP 3: Open a connection
try (Connection conn1 = DriverManager.getConnection(DB_URL, USER,pwd) {
conn1.setAutoCommit(true);
String Select = "select * from Persons where ID = " + ID;
Statement st1 = conn1.createStatement();
ResultSet rs1 = st1.executeQuery(Select);
// iterate through the java resultset
while (rs1.next()) {
int ID = rs1.getInt("ID");
if (NewID==ID) {
IdAlreadyExist = true;
}
}
conn1.close();
} catch (SQLException e1) {
System.out.println(e1);
}
if (IdAlreadyExist==false) {
//Insert new record code here
} else {
//Update existing record code here
}
Not OP's answer but as this was the first question that popped up for me in google, Id also like to add that users searching for this might need to reseed their table, which was the case for me
DBCC CHECKIDENT(tablename)
There could be several things causing this and it somewhat depends on what you have set up in your database.
First, you could be using a PK in the table that is also an FK to another table making the relationship 1-1. IN this case you may need to do an update rather than an insert. If you really can have only one address record for an order this may be what is happening.
Next you could be using some sort of manual process to determine the id ahead of time. The trouble with those manual processes is that they can create race conditions where two records gab the same last id and increment it by one and then the second one can;t insert.
Third, you query as it is sent to the database may be creating two records. To determine if this is the case, Run Profiler to see exactly what SQL code you are sending and if ti is a select instead of a values clause, then run the select and see if you have due to the joins gotten some records to be duplicated. IN any even when you are creating code on the fly like this the first troubleshooting step is ALWAYS to run Profiler and see if what got sent was what you expected to be sent.
Make sure if your table doesn't already have rows whose Primary Key values are same as the the Primary Key Id in your Query.
I am creating an SQLite database.
db.execSQL("CREATE TABLE " + DATABASE_TABLE + " ("
+ KEY_ROWID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ KEY_NAME + " TEXT NOT NULL, "
+ KEY_WORKED + " INTEGER, "
+ KEY_NOTE + " INTEGER);");
Is it possible to set the default value of KEY_NOTE (which is an integer) for every row created to be 0 (zero)? If so, what should be the correct code?
Use the SQLite keyword default
db.execSQL("CREATE TABLE " + DATABASE_TABLE + " ("
+ KEY_ROWID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ KEY_NAME + " TEXT NOT NULL, "
+ KEY_WORKED + " INTEGER, "
+ KEY_NOTE + " INTEGER DEFAULT 0);");
This link is useful: http://www.sqlite.org/lang_createtable.html
A column with default value:
CREATE TABLE <TableName>(
...
<ColumnName> <Type> DEFAULT <DefaultValue>
...
)
<DefaultValue> is a placeholder for a:
value literal
( expression )
Examples:
Count INTEGER DEFAULT 0,
LastSeen TEXT DEFAULT (datetime('now'))
It happens that I'm just starting to learn coding and I needed something similar as you have just asked in SQLite (I´m using [SQLiteStudio] (3.1.1)).
It happens that you must define the column's 'Constraint' as 'Not Null' then entering your desired definition using 'Default' 'Constraint' or it will not work (I don't know if this is an SQLite or the program requirment).
Here is the code I used:
CREATE TABLE <MY_TABLE> (
<MY_TABLE_KEY> INTEGER UNIQUE
PRIMARY KEY,
<MY_TABLE_SERIAL> TEXT DEFAULT (<MY_VALUE>)
NOT NULL
<THE_REST_COLUMNS>
);