[Tdbc] Add Tizen.Data.Tdbc (#5145)
authorjeremy-jang <35089715+jeremy-jang@users.noreply.github.com>
Mon, 24 Apr 2023 23:22:41 +0000 (08:22 +0900)
committerGitHub <noreply@github.com>
Mon, 24 Apr 2023 23:22:41 +0000 (08:22 +0900)
* [Tdbc] Add Tizen.Data.Tdbc

Provides a standard C# API for accessing various database for Tizen platform.

Signed-off-by: Sangyoon Jang <jeremy.jang@samsung.com>
* [Tdbc] Add a default sqlite driver

Signed-off-by: Sangyoon Jang <jeremy.jang@samsung.com>
* [Tdbc] Add enum OperationType

Signed-off-by: Sangyoon Jang <jeremy.jang@samsung.com>
* [Tdbc] Fix Tdbc

- Fix doxygen comments
- Fix nameing of interfaces

Signed-off-by: Sangyoon Jang <jeremy.jang@samsung.com>
* [Tdbc] Fix doxygen

Signed-off-by: Sangyoon Jang <jeremy.jang@samsung.com>
* [Tdbc] Fix Tdbc

- Fix doxygen.
- Remove rowid from RecordChangedEventArgs.

Signed-off-by: Sangyoon Jang <jeremy.jang@samsung.com>
* [Tdbc] Fix wrong implemented IEnumeratable

Signed-off-by: Sangyoon Jang <jeremy.jang@samsung.com>
* [Tdbc] Fix RecordChangedEventArgs

Add a Record to event.

Signed-off-by: Sangyoon Jang <jeremy.jang@samsung.com>
* [Tdbc] Fix TDBC

- Add namespace document.
- Fix dictionary type of Statement class.
- Fix some doxygen comments.

Signed-off-by: Sangyoon Jang <jeremy.jang@samsung.com>
* [Tdbc] Add remarks for privileges

Signed-off-by: Sangyoon Jang <jeremy.jang@samsung.com>
* [Tdbc] Fix Connection class

- IsOpened() -> IsOpen()
- Add readonly keyworkd at lock object

Signed-off-by: Sangyoon Jang <jeremy.jang@samsung.com>
* [Tdbc] Rename files

Signed-off-by: Sangyoon Jang <jeremy.jang@samsung.com>
---------

Signed-off-by: Sangyoon Jang <jeremy.jang@samsung.com>
19 files changed:
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/Connection.cs [new file with mode: 0644]
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/Interop/Interop.Sqlite.cs [new file with mode: 0644]
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/Record.cs [new file with mode: 0644]
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/ResultSet.cs [new file with mode: 0644]
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/Statement.cs [new file with mode: 0644]
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/Tizen.Data.Tdbc.Driver.Sqlite.csproj [new file with mode: 0644]
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.sln [new file with mode: 0644]
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/ConnectionUriBuilder.cs [new file with mode: 0644]
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/DriverManager.cs [new file with mode: 0644]
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/IConnection.cs [new file with mode: 0644]
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/IRecord.cs [new file with mode: 0644]
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/IResultSet.cs [new file with mode: 0644]
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/IStatement.cs [new file with mode: 0644]
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/OperationType.cs [new file with mode: 0644]
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/RecordChangedEventArgs.cs [new file with mode: 0644]
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/Sql.cs [new file with mode: 0644]
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/Tizen.Data.Tdbc.csproj [new file with mode: 0644]
src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/TransactionGuard.cs [new file with mode: 0644]
src/Tizen.Data.Tdbc/doc/api/Tizen.Data.Tdbc.md [new file with mode: 0644]

diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/Connection.cs b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/Connection.cs
new file mode 100644 (file)
index 0000000..7bcc8bd
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.ComponentModel;
+using System.Linq;
+
+namespace Tizen.Data.Tdbc.Driver.Sqlite
+{
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    internal class Connection : IConnection
+    {
+        private bool _opened;
+        private IntPtr _db;
+        private bool disposedValue;
+        private readonly object _lock = new object();
+        private EventHandler<RecordChangedEventArgs> _recordChanged;
+
+
+        static Connection()
+        {
+            Interop.Sqlite.Init();
+        }
+
+        public Connection()
+        {
+        }
+
+        public event EventHandler<RecordChangedEventArgs> RecordChanged
+        {
+            add
+            {
+                lock (_lock)
+                {
+                    _recordChanged += value;
+                }
+            }
+
+            remove
+            {
+                lock (_lock)
+                {
+                    _recordChanged -= value;
+                }
+            }
+        }
+
+        public void Open(String openString)
+        {
+            Open(new Uri(openString));
+        }
+        
+        public void Close()
+        {
+            if (_opened)
+            {
+                Interop.Sqlite.UpdateHook(_db, null, IntPtr.Zero);
+                Interop.Sqlite.Close(_db);
+                _opened = false;
+            }
+        }
+
+        private void UpdateHookCallback(IntPtr data, int action, string db_name, string table_name, long rowid)
+        {
+            OperationType operationType = OperationType.None;
+            switch (((Interop.Sqlite.UpdateHookAction)action))
+            {
+                case Interop.Sqlite.UpdateHookAction.SQLITE_UPDATE:
+                    operationType = OperationType.Update;
+                    break;
+                case Interop.Sqlite.UpdateHookAction.SQLITE_INSERT:
+                    operationType = OperationType.Insert;
+                    break;
+                case Interop.Sqlite.UpdateHookAction.SQLITE_DELETE:
+                    operationType = OperationType.Delete;
+                    break;
+            }
+
+            Sql sql = new Sql(string.Format("SELECT * from {0} WHERE rowid = {1}", table_name, rowid));
+            IRecord record = CreateStatement().ExecuteQuery(sql).FirstOrDefault();
+
+            RecordChangedEventArgs ev = new RecordChangedEventArgs(operationType, db_name, table_name, record);
+            _recordChanged?.Invoke(this, ev);
+        }
+
+        internal IntPtr GetHandle()
+        {
+            return _db;
+        }
+
+        public void Open(Uri uri)
+        {
+            if (IsOpen())
+                return;
+
+            if (uri.Scheme != "tdbc")
+                throw new ArgumentException("Wrong scheme:" + uri.Scheme);
+
+            if (uri.Host != "localhost")
+                throw new ArgumentException("Host should be 'localhost':" + uri.Host);
+
+            string query = uri.Query;
+            var queryDictionary = System.Web.HttpUtility.ParseQueryString(query);
+            int mode = (int)Interop.Sqlite.OpenParameter.SQLITE_OPEN_READWRITE |
+                (int)Interop.Sqlite.OpenParameter.SQLITE_OPEN_CREATE;
+
+            if (queryDictionary.Get("mode") == "ro")
+            {
+                mode = (int)Interop.Sqlite.OpenParameter.SQLITE_OPEN_READONLY;
+            }
+            else if (queryDictionary.Get("mode") == "rw")
+            {
+                mode = (int)Interop.Sqlite.OpenParameter.SQLITE_OPEN_READWRITE;
+            }
+            else if (queryDictionary.Get("mode") == "rwc")
+            {
+                mode = (int)Interop.Sqlite.OpenParameter.SQLITE_OPEN_READWRITE |
+                    (int)Interop.Sqlite.OpenParameter.SQLITE_OPEN_CREATE;
+            }
+            else if (queryDictionary.Get("mode") == "memory")
+            {
+                mode = (int)Interop.Sqlite.OpenParameter.SQLITE_OPEN_MEMORY;
+            }
+
+            if (queryDictionary.Get("cache") == "shared")
+            {
+                mode |= (int)Interop.Sqlite.OpenParameter.SQLITE_OPEN_SHAREDCACHE;
+            }
+            else if (queryDictionary.Get("cache") == "private")
+            {
+                mode |= (int)Interop.Sqlite.OpenParameter.SQLITE_OPEN_PRIVATECACHE;
+            }
+
+            int ret = Interop.Sqlite.OpenV2(uri.LocalPath, out _db, mode, IntPtr.Zero);
+            if (ret != (int)Interop.Sqlite.ResultCode.SQLITE_OK)
+                throw new InvalidOperationException("code:" + ret);
+
+            Interop.Sqlite.UpdateHook(_db, UpdateHookCallback, IntPtr.Zero);
+            _opened = true;
+        }
+
+        public IStatement CreateStatement()
+        {
+            if (!IsOpen())
+                throw new InvalidOperationException("Not opened");
+
+            return new Statement(this);
+        }
+
+        public bool IsOpen()
+        {
+            return _opened;
+        }
+
+        protected virtual void Dispose(bool disposing)
+        {
+            if (!disposedValue)
+            {
+                if (disposing)
+                {
+                }
+
+                if (_opened)
+                    Close();
+                disposedValue = true;
+            }
+        }
+
+         ~Connection()
+         {
+             Dispose(disposing: false);
+         }
+
+        public void Dispose()
+        {
+            Dispose(disposing: true);
+            GC.SuppressFinalize(this);
+        }
+    }
+}
diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/Interop/Interop.Sqlite.cs b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/Interop/Interop.Sqlite.cs
new file mode 100644 (file)
index 0000000..b01e04d
--- /dev/null
@@ -0,0 +1,155 @@
+using System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+    const string Lib = "libsqlite3.so.0";
+
+    internal static partial class Sqlite
+    {
+        internal static void Init()
+        {
+        }
+
+        [Flags]
+        internal enum OpenParameter : int
+        {
+            SQLITE_OPEN_READONLY = 0x00000001,
+            SQLITE_OPEN_READWRITE = 0x00000002,
+            SQLITE_OPEN_CREATE = 0x00000004,
+            SQLITE_OPEN_DELETEONCLOSE = 0x00000008,
+            SQLITE_OPEN_EXCLUSIVE = 0x00000010,
+            SQLITE_OPEN_AUTOPROXY = 0x00000020,
+            SQLITE_OPEN_URI = 0x00000040,
+            SQLITE_OPEN_MEMORY = 0x00000080,
+            SQLITE_OPEN_MAIN_DB = 0x00000100,
+            SQLITE_OPEN_TEMP_DB = 0x00000200,
+            SQLITE_OPEN_TRANSIENT_DB = 0x00000400,
+            SQLITE_OPEN_MAIN_JOURNAL = 0x00000800,
+            SQLITE_OPEN_TEMP_JOURNAL = 0x00001000,
+            SQLITE_OPEN_SUBJOURNAL = 0x00002000,
+            SQLITE_OPEN_MASTER_JOURNAL = 0x00004000,
+            SQLITE_OPEN_NOMUTEX = 0x00008000,
+            SQLITE_OPEN_FULLMUTEX = 0x00010000,
+            SQLITE_OPEN_SHAREDCACHE = 0x00020000,
+            SQLITE_OPEN_PRIVATECACHE = 0x00040000,
+            SQLITE_OPEN_WAL = 0x00080000
+        }
+
+        internal enum ResultCode : int
+        {
+            SQLITE_OK = 0,
+            SQLITE_ERROR = 1,
+            SQLITE_INTERNAL = 2,
+            SQLITE_PERM = 3,
+            SQLITE_ABORT = 4,
+            SQLITE_BUSY = 5,
+            SQLITE_LOCKED = 6,
+            SQLITE_NOMEM = 7,
+            SQLITE_READONLY = 8,
+            SQLITE_INTERRUPT = 9,
+            SQLITE_IOERR = 10,
+            SQLITE_CORRUPT = 11,
+            SQLITE_NOTFOUND = 12,
+            SQLITE_FULL = 13,
+            SQLITE_CANTOPEN = 14,
+            SQLITE_PROTOCOL = 15,
+            SQLITE_EMPTY = 16,
+            SQLITE_SCHEMA = 17,
+            SQLITE_TOOBIG = 18,
+            SQLITE_CONSTRAINT = 19,
+            SQLITE_MISMATCH = 20,
+            SQLITE_MISUSE = 21,
+            SQLITE_NOLFS = 22,
+            SQLITE_AUTH = 23,
+            SQLITE_FORMAT = 24,
+            SQLITE_RANGE = 25,
+            SQLITE_NOTADB = 26,
+            SQLITE_NOTICE = 27,
+            SQLITE_WARNING = 28,
+            SQLITE_ROW = 100,
+            SQLITE_DONE = 101
+        }
+
+        //int sqlite3_open_v2(const char* filename, sqlite3** ppDb, int flags, const char* zVfs)
+        [DllImport(Lib, EntryPoint = "sqlite3_open_v2")]
+        internal static extern int OpenV2(string filename, out IntPtr ppDb, int flags, IntPtr zVfs);
+
+        //int sqlite3_close_v2(sqlite3*)
+        [DllImport(Lib, EntryPoint = "sqlite3_close_v2")]
+        internal static extern int Close(IntPtr pDb);
+
+        //int sqlite3_prepare_v2(sqlite3* db, const char* zSql, int nByte, sqlite3_stmt** ppStmt, const char** pzTail)
+        [DllImport(Lib, EntryPoint = "sqlite3_prepare_v2")]
+        internal static extern int Prepare(IntPtr pDb, string zSql, int nByte, out IntPtr ppStmt, IntPtr pzTail);
+
+        //int sqlite3_bind_text(sqlite3_stmt*, int,const char*,int,void (*) (void*));
+        [DllImport(Lib, EntryPoint = "sqlite3_bind_text")]
+        internal static extern int BindText(IntPtr stmt, int pos, string text, int size, IntPtr mode);
+
+        //int sqlite3_bind_int(sqlite3_stmt*, int, int);
+        [DllImport(Lib, EntryPoint = "sqlite3_bind_int")]
+        internal static extern int BindInt(IntPtr stmt, int pos, int val);
+
+        //int sqlite3_bind_double(sqlite3_stmt*, int, double);
+        [DllImport(Lib, EntryPoint = "sqlite3_bind_double")]
+        internal static extern int BindDouble(IntPtr stmt, int pos, double val);
+
+        //int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void (*) (void*));
+        [DllImport(Lib, EntryPoint = "sqlite3_bind_blob")]
+        internal static extern int BindData(IntPtr stmt, int pos, byte[] val, int size, IntPtr mode);
+
+        //int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
+        [DllImport(Lib, EntryPoint = "sqlite3_bind_parameter_index")]
+        internal static extern int GetParameterIndex(IntPtr stmt, string zName);
+
+        //int sqlite3_step(sqlite3_stmt*)
+        [DllImport(Lib, EntryPoint = "sqlite3_step")]
+        internal static extern int Step(IntPtr stmt);
+
+        //int sqlite3_finalize(sqlite3_stmt *pStmt);
+        [DllImport(Lib, EntryPoint = "sqlite3_finalize")]
+        internal static extern int Finalize(IntPtr stmt);
+
+        //int sqlite3_changes(sqlite3*)
+        [DllImport(Lib, EntryPoint = "sqlite3_changes")]
+        internal static extern int Changes(IntPtr db);
+
+        //const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
+        [DllImport(Lib, EntryPoint = "sqlite3_column_text")]
+        internal static extern IntPtr ColumnText(IntPtr stmt, int col);
+
+        //int sqlite3_column_int(sqlite3_stmt*, int iCol);
+        [DllImport(Lib, EntryPoint = "sqlite3_column_int")]
+        internal static extern int ColumnInt(IntPtr stmt, int col);
+
+        //double sqlite3_column_double(sqlite3_stmt*, int iCol);
+        [DllImport(Lib, EntryPoint = "sqlite3_column_double")]
+        internal static extern double ColumnDouble(IntPtr stmt, int col);
+
+        //const void* sqlite3_column_blob(sqlite3_stmt *, int iCol);
+        [DllImport(Lib, EntryPoint = "sqlite3_column_blob")]
+        internal static extern IntPtr ColumnBlob(IntPtr stmt, int col);
+
+        //int sqlite3_reset(sqlite3_stmt* pStmt);
+        [DllImport(Lib, EntryPoint = "sqlite3_reset")]
+        internal static extern int Reset(IntPtr stmt);
+
+        //int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
+        [DllImport(Lib, EntryPoint = "sqlite3_column_bytes")]
+        internal static extern int ColumnBytes(IntPtr stmt, int col);
+
+        internal enum UpdateHookAction : int
+        {
+            SQLITE_DELETE = 9,
+            SQLITE_INSERT = 18,
+            SQLITE_UPDATE = 23
+        }
+
+        internal delegate void UpdateHookCallback(IntPtr data, int action, string db_name, string table_name, long rowid);
+
+        //void* sqlite3_update_hook(sqlite3*, void(*)(void* data, int action, char const* db_name, char const* table_name, sqlite3_int64 rowid), void* data);
+        [DllImport(Lib, EntryPoint = "sqlite3_update_hook")]
+        internal static extern IntPtr UpdateHook(IntPtr db, UpdateHookCallback cb, IntPtr data);
+    }
+}
diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/Record.cs b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/Record.cs
new file mode 100644 (file)
index 0000000..8047dd2
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+
+namespace Tizen.Data.Tdbc.Driver.Sqlite
+{
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    internal class Record : IRecord
+    {
+        private IntPtr _stmt;
+        IRecord IEnumerator<IRecord>.Current => this;
+        public object Current => this.Current;
+
+        internal Record(IntPtr stmt)
+        {
+            _stmt = stmt;
+        }
+
+        public bool MoveNext()
+        {
+            int ret = Interop.Sqlite.Step(_stmt);
+            if (ret != (int)Interop.Sqlite.ResultCode.SQLITE_ROW)
+                return false;
+
+            return true;
+        }
+
+        public void Reset()
+        {
+            Interop.Sqlite.Reset(_stmt);
+        }
+
+        public bool GetBool(int columnIndex)
+        {
+            int ret = Interop.Sqlite.ColumnInt(_stmt, columnIndex);
+            if (ret == 0)
+                return false;
+            return true;
+        }
+
+        public byte[] GetData(int columnIndex)
+        {
+            IntPtr raw = Interop.Sqlite.ColumnBlob(_stmt, columnIndex);
+            if (raw == IntPtr.Zero)
+                return null;
+
+            int size = Interop.Sqlite.ColumnBytes(_stmt, columnIndex);
+            byte[] ret = new byte[size];
+            Marshal.Copy((IntPtr)raw, (byte[])ret, 0, (int)size);
+
+            return ret;
+        }
+
+        public double GetDouble(int columnIndex)
+        {
+            return Interop.Sqlite.ColumnDouble(_stmt, columnIndex);
+        }
+
+        public int GetInt(int columnIndex)
+        {
+            return Interop.Sqlite.ColumnInt(_stmt, columnIndex);
+        }
+
+        public string GetString(int columnIndex)
+        {
+            IntPtr raw = Interop.Sqlite.ColumnText(_stmt, columnIndex);
+
+            return Marshal.PtrToStringAnsi(raw);
+        }
+
+        public char GetChar(int columnIndex)
+        {
+            throw new NotImplementedException();
+        }
+
+        public char[] GetChars(int columnIndex)
+        {
+            throw new NotImplementedException();
+        }
+
+        public DateTime GetDate(int columnIndex)
+        {
+            throw new NotImplementedException();
+        }
+
+        public DateTime GetDateTime(int columnIndex)
+        {
+            throw new NotImplementedException();
+        }
+
+        public decimal GetDecimal(int columnIndex)
+        {
+            throw new NotImplementedException();
+        }
+
+        public float GetFloat(int columnIndex)
+        {
+            throw new NotImplementedException();
+        }
+
+        public short GetInt16(int columnIndex)
+        {
+            throw new NotImplementedException();
+        }
+
+        public long GetInt64(int columnIndex)
+        {
+            throw new NotImplementedException();
+        }
+
+        public TimeSpan GetTime(int columnIndex)
+        {
+            throw new NotImplementedException();
+        }
+
+        public string GetName(int columnIndex)
+        {
+            throw new NotImplementedException();
+        }
+
+        public void Dispose()
+        {
+        }
+    }
+}
diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/ResultSet.cs b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/ResultSet.cs
new file mode 100644 (file)
index 0000000..2ce7c6c
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace Tizen.Data.Tdbc.Driver.Sqlite
+{
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    internal class ResultSet : IResultSet
+    {
+        private IntPtr _stmt;
+        private bool disposedValue;
+        private Connection _conn;
+
+        private void Clear()
+        {
+            if (_stmt != IntPtr.Zero)
+            {
+                Interop.Sqlite.Finalize(_stmt);
+                _stmt = IntPtr.Zero;
+            }
+        }
+
+        internal ResultSet(IntPtr stmt, Connection conn)
+        {
+            _stmt = stmt;
+            _conn = conn;
+        }
+
+        public IEnumerator<IRecord> GetEnumerator()
+        {
+            return new Record(_stmt);
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return this.GetEnumerator();
+        }
+
+        protected virtual void Dispose(bool disposing)
+        {
+            if (!disposedValue)
+            {
+                if (disposing)
+                {
+                }
+
+                Clear();
+                disposedValue = true;
+            }
+        }
+
+        ~ResultSet()
+        {
+             Dispose(disposing: false);
+        }
+
+        public void Dispose()
+        {
+            Dispose(disposing: true);
+            GC.SuppressFinalize(this);
+        }
+    }
+}
diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/Statement.cs b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/Statement.cs
new file mode 100644 (file)
index 0000000..9114777
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.ComponentModel;
+using System.Threading.Tasks;
+
+namespace Tizen.Data.Tdbc.Driver.Sqlite
+{
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class Statement : IStatement
+    {
+        Connection _conn;
+        private IntPtr _stmt;
+        private bool disposedValue;
+
+        private void Clear()
+        {
+            if (_stmt != IntPtr.Zero)
+            {
+                Interop.Sqlite.Finalize(_stmt);
+                _stmt = IntPtr.Zero;
+            }
+        }
+
+        private bool Prepare(Sql sql)
+        {
+            Clear();
+            Console.WriteLine("Prepare: " + sql.Command);
+            int ret = Interop.Sqlite.Prepare(_conn.GetHandle(), sql.Command, -1, out _stmt, IntPtr.Zero);
+            if (ret != (int)Interop.Sqlite.ResultCode.SQLITE_OK)
+            {
+                Console.WriteLine("Prepare: failed " + ret);
+                return false;
+            }
+
+            foreach (var i in sql.Bindings)
+            {
+                string key = i.Key;
+                Object obj = i.Value;
+                int pos = Interop.Sqlite.GetParameterIndex(_stmt, key);
+                if (pos == 0)
+                    throw new InvalidOperationException("Invalid binding");
+
+                if (typeof(int) == obj.GetType())
+                {
+                    Interop.Sqlite.BindInt(_stmt, pos, (int)obj);
+                }
+                else if (typeof(bool) == obj.GetType())
+                {
+                    bool val = (bool)obj;
+                    Interop.Sqlite.BindInt(_stmt, pos, val ? 1 : 0);
+                }
+                else if (typeof(double) == obj.GetType())
+                {
+                    Interop.Sqlite.BindDouble(_stmt, pos, (double)obj);
+                }
+                else if (typeof(string) == obj.GetType())
+                {
+                    Interop.Sqlite.BindText(_stmt, pos, (string)obj, -1, (IntPtr)(-1));
+                }
+                else if (typeof(byte[]) == obj.GetType())
+                {
+                    if (obj != null)
+                        Interop.Sqlite.BindData(_stmt, pos, (byte[])obj, ((byte[])obj).Length, IntPtr.Zero);
+                }
+            }
+
+            return true;
+        }
+
+        internal Statement(Connection conn)
+        {
+            _conn = conn;
+        }
+
+        public bool Execute(Sql sql)
+        {
+            if (!Prepare(sql))
+                return false;
+            int ret = Interop.Sqlite.Step(_stmt);
+            Console.WriteLine("Execute: " + ret); 
+            if (ret != (int)Interop.Sqlite.ResultCode.SQLITE_ROW &&
+                ret != (int)Interop.Sqlite.ResultCode.SQLITE_DONE)
+                return false;
+            return true;
+        }
+
+        public int ExecuteUpdate(Sql sql)
+        {
+            if (!Prepare(sql))
+                return 0;
+            int ret = Interop.Sqlite.Step(_stmt);
+            if (ret != (int)Interop.Sqlite.ResultCode.SQLITE_ROW &&
+                ret != (int)Interop.Sqlite.ResultCode.SQLITE_DONE)
+                return 0;
+            return Interop.Sqlite.Changes(_conn.GetHandle());
+        }
+
+        public IResultSet ExecuteQuery(Sql sql)
+        {
+            bool prepared = Prepare(sql);
+
+            if (!prepared)
+                throw new InvalidOperationException("Couldn't prepare");
+
+            var set = new ResultSet(_stmt, _conn);
+            _stmt = IntPtr.Zero;
+
+            return set;
+        }
+
+        public async Task<IResultSet> ExecuteQueryAsync(Sql sql)
+        {
+            return await Task.Run(() =>
+            {
+                return ExecuteQuery(sql);
+            }).ConfigureAwait(false);
+        }
+
+        public async Task<int> ExecuteUpdateAsync(Sql sql)
+        {
+            return await Task.Run(() =>
+            {
+                return ExecuteUpdate(sql);
+            }).ConfigureAwait(false);
+        }
+
+        public async Task<bool> ExecuteAsync(Sql sql)
+        {
+            return await Task.Run(() =>
+            {
+                return Execute(sql);
+            }).ConfigureAwait(false);
+        }
+
+        protected virtual void Dispose(bool disposing)
+        {
+            if (!disposedValue)
+            {
+                if (disposing)
+                {
+                }
+
+                Clear();
+                disposedValue = true;
+            }
+        }
+
+        ~Statement()
+        {
+            Dispose(disposing: false);
+        }
+
+        public void Dispose()
+        {
+            Dispose(disposing: true);
+            GC.SuppressFinalize(this);
+        }
+    }
+}
diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/Tizen.Data.Tdbc.Driver.Sqlite.csproj b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.Driver.Sqlite/Tizen.Data.Tdbc.Driver.Sqlite.csproj
new file mode 100644 (file)
index 0000000..c20a09c
--- /dev/null
@@ -0,0 +1,11 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard2.0</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\Tizen.Data.Tdbc\Tizen.Data.Tdbc.csproj" />
+  </ItemGroup>
+
+</Project>
diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.sln b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc.sln
new file mode 100644 (file)
index 0000000..3d66505
--- /dev/null
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.3.32819.101
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tizen.Data.Tdbc", "Tizen.Data.Tdbc\Tizen.Data.Tdbc.csproj", "{218E5B96-2231-4C8A-9980-5858EDD84D29}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tizen.Data.Tdbc.Driver.Sqlite", "Tizen.Data.Tdbc.Driver.Sqlite\Tizen.Data.Tdbc.Driver.Sqlite.csproj", "{7798035E-735F-4489-9795-06A0918532FB}"
+EndProject
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Debug|Any CPU = Debug|Any CPU
+               Release|Any CPU = Release|Any CPU
+       EndGlobalSection
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {218E5B96-2231-4C8A-9980-5858EDD84D29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {218E5B96-2231-4C8A-9980-5858EDD84D29}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {218E5B96-2231-4C8A-9980-5858EDD84D29}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {218E5B96-2231-4C8A-9980-5858EDD84D29}.Release|Any CPU.Build.0 = Release|Any CPU
+               {7798035E-735F-4489-9795-06A0918532FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {7798035E-735F-4489-9795-06A0918532FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {7798035E-735F-4489-9795-06A0918532FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {7798035E-735F-4489-9795-06A0918532FB}.Release|Any CPU.Build.0 = Release|Any CPU
+       EndGlobalSection
+       GlobalSection(SolutionProperties) = preSolution
+               HideSolutionNode = FALSE
+       EndGlobalSection
+       GlobalSection(ExtensibilityGlobals) = postSolution
+               SolutionGuid = {57406145-A84A-4F50-99B7-CCFCE239B84A}
+       EndGlobalSection
+EndGlobal
diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/ConnectionUriBuilder.cs b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/ConnectionUriBuilder.cs
new file mode 100644 (file)
index 0000000..a8f2369
--- /dev/null
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Specialized;
+using System.Web;
+
+namespace Tizen.Data.Tdbc
+{
+    /// <summary>
+    /// Provides a simple way to create and manage the contents of connection uri used by the Connection class.
+    /// </summary>
+    /// <since_tizen> 11 </since_tizen>
+    public class ConnectionUriBuilder : NameValueCollection
+    {
+        private UriBuilder _uriBuilder = new UriBuilder();
+
+        /// <summary>
+        /// The constructor of ConnectionUriBuilder class.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        public ConnectionUriBuilder()
+        {
+            _uriBuilder.Scheme = "tdbc";
+        }
+
+        /// <summary>
+        /// Gets the Uri object.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        public Uri Uri
+        {
+            get
+            {
+                var query = HttpUtility.ParseQueryString(String.Empty);
+                foreach (string key in this)
+                {
+                    string value = this[key];
+                    query.Add(key, value);
+                }
+                _uriBuilder.Query = query.ToString();
+                return _uriBuilder.Uri;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the host name or IP address of a database server.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        public string Host
+        {
+            get => _uriBuilder.Host;
+            set => _uriBuilder.Host = value;
+        }
+
+        /// <summary>
+        /// Gets or sets port number of database server.
+        /// </summary>
+        /// <exception cref="ArgumentOutOfRangeException">The port number is out of range.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        public int Port
+        {
+            get => _uriBuilder.Port;
+            set => _uriBuilder.Port = value;
+        }
+
+        /// <summary>
+        /// Gets or sets name of the driver.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        public string Driver
+        {
+            get => _uriBuilder.Path;
+            set => _uriBuilder.Path = value;
+        }
+
+        /// <summary>
+        /// Gets or sets the name of the user that accesses the database.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        public string UserName
+        {
+            get => _uriBuilder.UserName;
+            set => _uriBuilder.UserName = value;
+        }
+
+        /// <summary>
+        /// Gets or sets the name of the password of the user.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        public string Password
+        {
+            get => _uriBuilder.Password;
+            set => _uriBuilder.Password = value;
+        }
+
+        /// <summary>
+        /// Gets or sets value of key.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        public new string this[string key]
+        {
+            get => base.Get(key);
+            set => base.Set(key, value);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/DriverManager.cs b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/DriverManager.cs
new file mode 100644 (file)
index 0000000..d7c1e11
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Reflection;
+
+namespace Tizen.Data.Tdbc
+{
+    /// <summary>
+    /// DriverManager loads TDBC drivers and gets connections to databases.
+    /// </summary>
+    /// <since_tizen> 11 </since_tizen>
+    public class DriverManager
+    {
+        private static string _driverName;
+        private static Assembly _driverAssembly;
+
+        /// <summary>
+        /// Get connection of registered database and open the database.
+        /// </summary>
+        /// <param name="uri">The uri represents database to connect.</param>
+        /// <returns>The connection object.</returns>
+        /// <remarks>
+        /// If the driver uses database at filesystem, such as media storage or external storage,
+        /// you need to declare a proper privilege such as http://tizen.org/privileges/mediastorage or http://tizen.org/privileges/externalstorage.
+        /// </remarks>
+        /// <exception cref="InvalidOperationException">No driver registered.</exception>
+        /// <exception cref="SystemException">Failed to open database connection.</exception>
+        /// <exception cref="UnauthorizedAccessException">.The application doesn't have permission or privilege to access database.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        public static IConnection GetConnection(Uri uri)
+        {
+            if (_driverAssembly == null)
+            {
+                throw new InvalidOperationException("No TDBC driver registered.");
+            }
+
+            IConnection conn;
+            try
+            {
+                conn = (IConnection)_driverAssembly.CreateInstance(_driverName + ".Connection");
+            }
+            catch (Exception ex)
+            {
+                throw new SystemException("Failed to open connection due to: " + ex);
+            }
+
+            conn.Open(uri);
+            return conn;
+        }
+
+        /// <summary>
+        /// Get connection of registered database and open the database.
+        /// </summary>
+        /// <param name="connectionString">The string for connect and open database.</param>
+        /// <returns>The connection object.</returns>
+        /// <remarks>
+        /// If the driver uses database at filesystem, such as media storage or external storage,
+        /// you need to declare a proper privilege such as http://tizen.org/privileges/mediastorage or http://tizen.org/privileges/externalstorage.
+        /// </remarks>
+        /// <exception cref="InvalidOperationException">No driver registered.</exception>
+        /// <exception cref="SystemException">Failed to open database connection.</exception>
+        /// <exception cref="UnauthorizedAccessException">.The application doesn't have permission or privilege to access database.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        public static IConnection GetConnection(String connectionString)
+        {
+            if (_driverAssembly == null)
+                throw new InvalidOperationException("No TDBC driver registered.");
+
+            IConnection conn;
+            try
+            {
+                conn = (IConnection)_driverAssembly.CreateInstance(_driverName + ".Connection");
+            }
+            catch (Exception ex)
+            {
+                throw new SystemException("Failed to open connection due to: " + ex);
+            }
+
+            conn.Open(connectionString);
+            return conn;
+        }
+
+        /// <summary>
+        /// Registers and loads the TDBC driver with the given driver name.
+        /// </summary>
+        /// <param name="name">The name of TDBC driver.</param>
+        /// <exception cref="ArgumentNullException">The given name is null.</exception>
+        /// <exception cref="ArgumentException">The given name is zero-length string.</exception>
+        /// <exception cref="FileNotFoundException">The given driver name is not found.</exception>
+        /// <exception cref="FileLoadException">The given driver could not be loaded.</exception>
+        /// <exception cref="BadImageFormatException">The given driver is not a valid assembly.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        public static void RegisterDriver(string name)
+        {
+            _driverName = name;
+            _driverAssembly = Assembly.Load(_driverName);
+        }
+    }
+}
diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/IConnection.cs b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/IConnection.cs
new file mode 100644 (file)
index 0000000..2fc90c2
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+
+namespace Tizen.Data.Tdbc
+{
+    /// <summary>
+    /// TDBC Interface for connecting with a database.
+    /// </summary>
+    /// <since_tizen> 11 </since_tizen>
+    public interface IConnection : IDisposable
+    {
+        /// <summary>
+        /// Opens the database.
+        /// </summary>
+        /// <param name="uri">The URI represents database to connect.</param>
+        /// <remarks>
+        /// If the driver uses database at filesystem, such as media storage or external storage,
+        /// you need to declare a proper privilege such as http://tizen.org/privileges/mediastorage or http://tizen.org/privileges/externalstorage.
+        /// </remarks>
+        /// <exception cref="ArgumentException">The input URI is invalid.</exception>
+        /// <exception cref="InvalidOperationException">The drvier open is failed.</exception>
+        /// <exception cref="UnauthorizedAccessException">.The application doesn't have permission or privilege to access database.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        void Open(Uri uri);
+
+        /// <summary>
+        /// Opens the database.
+        /// </summary>
+        /// <param name="openString">The URI represents database to connect.</param>
+        /// <remarks>
+        /// If the driver uses database at filesystem, such as media storage or external storage,
+        /// you need to declare a proper privilege such as http://tizen.org/privileges/mediastorage or http://tizen.org/privileges/externalstorage.
+        /// </remarks>
+        /// <exception cref="ArgumentException">The input openString is invalid.</exception>
+        /// <exception cref="InvalidOperationException">The drvier open is failed.</exception>
+        /// <exception cref="UnauthorizedAccessException">.The application doesn't have permission or privilege to access database.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        void Open(String openString);
+
+        /// <summary>
+        /// Closes the database.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        void Close();
+
+        /// <summary>
+        /// Returns that the database is opened or not.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        bool IsOpen();
+
+        /// <summary>
+        /// Creates a statement object associated with the connection.
+        /// </summary>
+        /// <exception cref="InvalidOperationException">The connection is not opened.</exception>
+        /// <returns>The statement object.</returns>
+        /// <since_tizen> 11 </since_tizen>
+        IStatement CreateStatement();
+
+        /// <summary>
+        /// The event occurs when record changed.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        event EventHandler<RecordChangedEventArgs> RecordChanged;
+    }
+}
diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/IRecord.cs b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/IRecord.cs
new file mode 100644 (file)
index 0000000..e48d51e
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+
+namespace Tizen.Data.Tdbc
+{
+    /// <summary>
+    /// Record class. This class provides the result of query.
+    /// </summary>
+    /// <since_tizen> 11 </since_tizen>
+    public interface IRecord : IEnumerator<IRecord>
+    {
+        /// <summary>
+        /// Get integer type value from the record with given index.
+        /// </summary>
+        /// <param name="columnIndex">The index of value.</param>
+        /// <exception cref="ArgumentOutOfRangeException">The given index is out of range.</exception>
+        /// <exception cref="NotSupportedException">This type is not supported.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        int GetInt(int columnIndex);
+
+        /// <summary>
+        /// Get string type value from the record with given index.
+        /// </summary>
+        /// <param name="columnIndex">The index of value.</param>
+        /// <exception cref="ArgumentOutOfRangeException">The given index is out of range.</exception>
+        /// <exception cref="NotSupportedException">This type is not supported.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        string GetString(int columnIndex);
+
+        /// <summary>
+        /// Get double type value from the record with given index.
+        /// </summary>
+        /// <param name="columnIndex">The index of value.</param>
+        /// <exception cref="ArgumentOutOfRangeException">The given index is out of range.</exception>
+        /// <exception cref="NotSupportedException">This type is not supported.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        double GetDouble(int columnIndex);
+
+        /// <summary>
+        /// Get bool type value from the record with given index.
+        /// </summary>
+        /// <param name="columnIndex">The index of value.</param>
+        /// <exception cref="ArgumentOutOfRangeException">The given index is out of range.</exception>
+        /// <exception cref="NotSupportedException">This type is not supported.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        bool GetBool(int columnIndex);
+
+        /// <summary>
+        /// Get blob type value from the record with given index.
+        /// </summary>
+        /// <param name="columnIndex">The index of value.</param>
+        /// <exception cref="ArgumentOutOfRangeException">The given index is out of range.</exception>
+        /// <exception cref="NotSupportedException">This type is not supported.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        byte[] GetData(int columnIndex);
+
+        /// <summary>
+        /// Get char type value from the record with given index.
+        /// </summary>
+        /// <param name="columnIndex">The index of value.</param>
+        /// <exception cref="ArgumentOutOfRangeException">The given index is out of range.</exception>
+        /// <exception cref="NotSupportedException">This type is not supported.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        char GetChar(int columnIndex);
+
+        /// <summary>
+        /// Get chars type value from the record with given index.
+        /// </summary>
+        /// <param name="columnIndex">The index of value.</param>
+        /// <exception cref="ArgumentOutOfRangeException">The given index is out of range.</exception>
+        /// <exception cref="NotSupportedException">This type is not supported.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        char[] GetChars(int columnIndex);
+
+        /// <summary>
+        /// Get date type value from the record with given index.
+        /// </summary>
+        /// <param name="columnIndex">The index of value.</param>
+        /// <exception cref="ArgumentOutOfRangeException">The given index is out of range.</exception>
+        /// <exception cref="NotSupportedException">This type is not supported.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        DateTime GetDate(int columnIndex);
+
+        /// <summary>
+        /// Get datetime type value from the record with given index.
+        /// </summary>
+        /// <param name="columnIndex">The index of value.</param>
+        /// <exception cref="ArgumentOutOfRangeException">The given index is out of range.</exception>
+        /// <exception cref="NotSupportedException">This type is not supported.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        DateTime GetDateTime(int columnIndex);
+
+        /// <summary>
+        /// Get decimal type value from the record with given index.
+        /// </summary>
+        /// <param name="columnIndex">The index of value.</param>
+        /// <exception cref="ArgumentOutOfRangeException">The given index is out of range.</exception>
+        /// <exception cref="NotSupportedException">This type is not supported.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        Decimal GetDecimal(int columnIndex);
+
+        /// <summary>
+        /// Get float type value from the record with given index.
+        /// </summary>
+        /// <param name="columnIndex">The index of value.</param>
+        /// <exception cref="ArgumentOutOfRangeException">The given index is out of range.</exception>
+        /// <exception cref="NotSupportedException">This type is not supported.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        float GetFloat(int columnIndex);
+
+        /// <summary>
+        /// Get 16-bit signed integer type value from the record with given index.
+        /// </summary>
+        /// <param name="columnIndex">The index of value.</param>
+        /// <exception cref="ArgumentOutOfRangeException">The given index is out of range.</exception>
+        /// <exception cref="NotSupportedException">This type is not supported.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        short GetInt16(int columnIndex);
+
+        /// <summary>
+        /// Get 64-bit signed integer type value from the record with given index.
+        /// </summary>
+        /// <param name="columnIndex">The index of value.</param>
+        /// <exception cref="ArgumentOutOfRangeException">The given index is out of range.</exception>
+        /// <exception cref="NotSupportedException">This type is not supported.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        long GetInt64(int columnIndex);
+
+        /// <summary>
+        /// Get time type value from the record with given index.
+        /// </summary>
+        /// <param name="columnIndex">The index of value.</param>
+        /// <exception cref="ArgumentOutOfRangeException">The given index is out of range.</exception>
+        /// <exception cref="NotSupportedException">This type is not supported.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        TimeSpan GetTime(int columnIndex);
+
+        /// <summary>
+        /// Get the name from the record with given index.
+        /// </summary>
+        /// <param name="columnIndex">The index of value.</param>
+        /// <exception cref="ArgumentOutOfRangeException">The given index is out of range.</exception>
+        /// <exception cref="NotSupportedException">This type is not supported.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        string GetName(int columnIndex);
+    }
+}
diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/IResultSet.cs b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/IResultSet.cs
new file mode 100644 (file)
index 0000000..ead0b07
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+
+namespace Tizen.Data.Tdbc
+{
+    /// <summary>
+    /// TDBC interface for a collection of IRecord items.
+    /// </summary>
+    /// <since_tizen> 11 </since_tizen>
+    public interface IResultSet : IEnumerable<IRecord>, IDisposable
+    {
+    }
+}
diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/IStatement.cs b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/IStatement.cs
new file mode 100644 (file)
index 0000000..95ba14d
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Threading.Tasks;
+
+namespace Tizen.Data.Tdbc
+{
+    /// <summary>
+    /// TDBC interface for representing statement.
+    /// </summary>
+    /// <since_tizen> 11 </since_tizen>
+    public interface IStatement : IDisposable
+    {
+        /// <summary>
+        /// Executes the given Sql.
+        /// </summary>
+        /// <param name="sql">The sql to execute.</param>
+        /// <returns>The ResultSet object that includes the result of the query.</returns>
+        /// <since_tizen> 11 </since_tizen>
+        IResultSet ExecuteQuery(Sql sql);
+
+        /// <summary>
+        /// Executes the given Sql.
+        /// </summary>
+        /// <param name="sql">The sql to execute.</param>
+        /// <returns>The number of rows updated.</returns>
+        /// <since_tizen> 11 </since_tizen>
+        int ExecuteUpdate(Sql sql);
+
+        /// <summary>
+        /// Executes the given Sql.
+        /// </summary>
+        /// <param name="sql">The sql to execute.</param>
+        /// <returns>True if the execution was success, otherwise false.</returns>
+        /// <since_tizen> 11 </since_tizen>
+        bool Execute(Sql sql);
+
+        /// <summary>
+        /// Executes the given Sql asynchronously.
+        /// </summary>
+        /// <param name="sql">The sql to execute.</param>
+        /// <returns>The ResultSet object that includes the result of the query.</returns>
+        /// <since_tizen> 11 </since_tizen>
+        Task<IResultSet> ExecuteQueryAsync(Sql sql);
+
+        /// <summary>
+        /// Executes the given Sql asynchronously.
+        /// </summary>
+        /// <param name="sql">The sql to execute.</param>
+        /// <returns>The number of rows updated.</returns>
+        /// <since_tizen> 11 </since_tizen>
+        Task<int> ExecuteUpdateAsync(Sql sql);
+
+        /// <summary>
+        /// Executes the given Sql asynchronously.
+        /// </summary>
+        /// <param name="sql">The sql to execute.</param>
+        /// <returns>True if the execution was success, otherwise false.</returns>
+        /// <since_tizen> 11 </since_tizen>
+        Task<bool> ExecuteAsync(Sql sql);
+    }
+}
diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/OperationType.cs b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/OperationType.cs
new file mode 100644 (file)
index 0000000..9a2995e
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Tizen.Data.Tdbc
+{
+    /// <summary>
+    /// Enumeration for the database operation type.
+    /// </summary>
+    /// <since_tizen> 11 </since_tizen>
+    public enum OperationType
+    {
+        /// <summary>
+        /// Invalid operation type.
+        /// </summary>
+        None = -1,
+        /// <summary>
+        /// Insert operation type.
+        /// </summary>
+        Insert,
+        /// <summary>
+        /// Update operation type.
+        /// </summary>
+        Update,
+        /// <summary>
+        /// Delete operation type.
+        /// </summary>
+        Delete
+    }
+}
diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/RecordChangedEventArgs.cs b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/RecordChangedEventArgs.cs
new file mode 100644 (file)
index 0000000..52ca0d0
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.ComponentModel;
+
+namespace Tizen.Data.Tdbc
+{
+    /// <summary>
+    /// RecordChangedEventArgs class. This class is an event arguments of the RecordChanged events.
+    /// </summary>
+    /// <since_tizen> 11 </since_tizen>
+    public class RecordChangedEventArgs : EventArgs
+    {
+        private readonly OperationType _operationType;
+        private readonly string _database;
+        private readonly string _table;
+        private readonly IRecord _record;
+
+        /// <summary>
+        /// Creates and initializes a new instance of type of the RecordChangedEventArgs class.
+        /// </summary>
+        /// <param name="operationType">The operation type of the changed record.</param>
+        /// <param name="database">The database of the changed record.</param>
+        /// <param name="table">The table of the changed record.</param>
+        /// <param name="record">The changed record.</param>
+        /// <since_tizen> 11 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public RecordChangedEventArgs(OperationType operationType, string database, string table, IRecord record)
+        {
+            _operationType = operationType;
+            _database = database;
+            _table = table;
+            _record = record;
+        }
+
+        /// <summary>
+        /// Gets the operation type of the record changed event.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        public OperationType OperationType { get { return _operationType; } }
+
+        /// <summary>
+        /// Gets the database name of the record chagned event.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        public string Database { get { return _database; } }
+
+        /// <summary>
+        /// Gets the table name of the record changed event.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        public string Table { get { return _table; } }
+
+        /// <summary>
+        /// Gets the changed record.
+        /// </summary>
+        /// <remarks>If the operation type is Delete, the changed record may by empty.</remarks>
+        /// <since_tizen> 11 </since_tizen>
+        public IRecord Record { get { return _record; } }
+    }
+}
diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/Sql.cs b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/Sql.cs
new file mode 100644 (file)
index 0000000..24748b7
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+
+namespace Tizen.Data.Tdbc
+{
+    /// <summary>
+    /// Represents a SQL query string.
+    /// </summary>
+    /// <since_tizen> 11 </since_tizen>
+    public class Sql
+    {
+        private string _sql;
+        private Dictionary<string, Object> _bindings = new Dictionary<string, Object>();
+
+        /// <summary>
+        /// The SQL command string.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        public string Command
+        {
+            get => _sql;
+        }
+
+        /// <summary>
+        /// The bindings as a name to value Dictionary.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        public IReadOnlyDictionary<string, Object> Bindings
+        {
+            get => _bindings;
+        }
+
+        /// <summary>
+        /// The constructor of Sql class.
+        /// </summary>
+        /// <param name="sql">The string of the query.</param>
+        /// <since_tizen> 11 </since_tizen>
+        public Sql(string sql)
+        {
+            _sql = sql;
+        }
+
+        /// <summary>
+        /// Binds the variable to the parameter.
+        /// </summary>
+        /// <param name="key">The key of the parameter.</param>
+        /// <param name="val">The string type value to bind.</param>
+        /// <returns>The sql object itself.</returns>
+        /// <since_tizen> 11 </since_tizen>
+        public Sql Bind(string key, string val)
+        {
+            _bindings.Add(key, new Tuple<Object, Type>(val,typeof(string)));
+            return this;
+        }
+
+        /// <summary>
+        /// Binds the variable to the parameter.
+        /// </summary>
+        /// <param name="key">The key of the parameter.</param>
+        /// <param name="val">The integer type value to bind.</param>
+        /// <returns>The sql object itself.</returns>
+        /// <since_tizen> 11 </since_tizen>
+        public Sql Bind(string key, int val)
+        {
+            _bindings.Add(key, new Tuple<Object, Type>(val, typeof(int)));
+            return this;
+        }
+
+        /// <summary>
+        /// Binds the variable to the parameter.
+        /// </summary>
+        /// <param name="key">The key of the parameter.</param>
+        /// <param name="val">The double type value to bind.</param>
+        /// <returns>The sql object itself.</returns>
+        /// <since_tizen> 11 </since_tizen>
+        public Sql Bind(string key, double val)
+        {
+            _bindings.Add(key, new Tuple<Object, Type>(val, typeof(double)));
+            return this;
+        }
+
+        /// <summary>
+        /// Binds the variable to the parameter.
+        /// </summary>
+        /// <param name="key">The key of the parameter.</param>
+        /// <param name="val">The boolean type value to bind.</param>
+        /// <returns>The sql object itself.</returns>
+        /// <since_tizen> 11 </since_tizen>
+        public Sql Bind(string key, bool val)
+        {
+            _bindings.Add(key, new Tuple<Object, Type>(val, typeof(bool)));
+            return this;
+        }
+
+        /// <summary>
+        /// Binds the variable to the parameter.
+        /// </summary>
+        /// <param name="key">The key of the parameter.</param>
+        /// <param name="val">The byte type value to bind.</param>
+        /// <returns>The sql object itself.</returns>
+        /// <since_tizen> 11 </since_tizen>
+        public Sql Bind(string key, byte[] val)
+        {
+            _bindings.Add(key, new Tuple<Object, Type>(val, typeof(byte[])));
+            return this;
+        }
+    }
+}
diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/Tizen.Data.Tdbc.csproj b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/Tizen.Data.Tdbc.csproj
new file mode 100644 (file)
index 0000000..9f5c4f4
--- /dev/null
@@ -0,0 +1,7 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard2.0</TargetFramework>
+  </PropertyGroup>
+
+</Project>
diff --git a/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/TransactionGuard.cs b/src/Tizen.Data.Tdbc/Tizen.Data.Tdbc/TransactionGuard.cs
new file mode 100644 (file)
index 0000000..72ed8fd
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+
+namespace Tizen.Data.Tdbc
+{
+    /// <summary>
+    /// This class helps the operation of the statement in a transacted way.
+    /// </summary>
+    /// <remarks>To finish the transaction, either call Commit() to apply database operations or Dispose() to rollback the transaction.</remarks>
+    /// <example>Usage:
+    /// <code>
+    /// using (var transaction = new TransationGuard(statement)) {
+    ///     ...
+    ///     transaction.Commit();
+    /// }
+    /// </code>
+    /// </example>
+    /// <since_tizen> 11 </since_tizen>
+    public class TransactionGuard : IDisposable
+    {
+        private IStatement _stmt;
+        private bool _commited;
+
+        /// <summary>
+        /// The flag representing whether the TransactionGuard is enabled.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        public bool IsEnabled { get; set; }
+
+        /// <summary>
+        /// The constructor of TransactionGuard class.
+        /// </summary>
+        /// <param name="statement"></param>
+        /// <param name="isEnabled"></param>
+        /// <since_tizen> 11 </since_tizen>
+        public TransactionGuard(IStatement statement, bool isEnabled = true)
+        {
+            _stmt = statement;
+            IsEnabled = isEnabled;
+            if (!IsEnabled)
+                return;
+            _stmt.Execute(new Sql("BEGIN DEFERRED"));
+        }
+
+        /// <summary>
+        /// Dispose the object. Rollback the transaction.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        public void Dispose()
+        {
+            if (!IsEnabled)
+                return;
+            if (_commited)
+                return;
+            _stmt.Execute(new Sql("ROLLBACK"));
+        }
+
+        /// <summary>
+        /// Commit the transaction.
+        /// </summary>
+        /// <since_tizen> 11 </since_tizen>
+        public void Commit()
+        {
+            if (!IsEnabled)
+                return;
+            if (_commited)
+                return;
+            if (!_stmt.Execute(new Sql("COMMIT")))
+            {
+                _stmt.Execute(new Sql("ROLLBACK"));
+            }
+            _commited = true;
+        }
+    }
+}
diff --git a/src/Tizen.Data.Tdbc/doc/api/Tizen.Data.Tdbc.md b/src/Tizen.Data.Tdbc/doc/api/Tizen.Data.Tdbc.md
new file mode 100644 (file)
index 0000000..6c227bf
--- /dev/null
@@ -0,0 +1,19 @@
+---
+uid: Tizen.Data.Tdbc
+summary: TDBC provides a uniform interface for accessing various database systems in Tizen applications.
+remarks: *content
+---
+## Overview
+Tizen.Data.Tdbc provides developers with access to the Tizen Database Connectivity (TDBC) layer. The TDBC layer provides a uniform interface for accessing various database systems in Tizen. With Tizen.Data.Tdbc, developers can easily connect to and perform operations on different database systems without having to learn each system's specific API.
+
+## Connecting to database
+To use a variant database driver with Tdbc, you'll need to register the driver before using the Tizen.Data.Tdbc.DriverManager.RegisterDriver() method. This method registers a driver with TDBC, making it available for use in connecting to specific databases.
+
+Here's an example of how to connect to Sqlite database:
+```cs
+using Tizen.Data.Tdbc;
+// ...
+
+DriverManager.RegisterDriver("Tizen.Data.Tdbc.Driver.Sqlite");
+var conn = DriverManager.GetConnection(new Uri("tdbc://localhost/sqlite_test.db?mode=rwc"));
+```