Merge remote-tracking branch 'origin/master' into tizen
[platform/core/csapi/tizenfx.git] / src / Tizen.Data.Tdbc.Driver.Sqlite / Tizen.Data.Tdbc.Driver.Sqlite / Statement.cs
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 using System;
18 using System.ComponentModel;
19 using System.Threading.Tasks;
20
21 namespace Tizen.Data.Tdbc.Driver.Sqlite
22 {
23     [EditorBrowsable(EditorBrowsableState.Never)]
24     public class Statement : IStatement
25     {
26         Connection _conn;
27         private IntPtr _stmt;
28         private bool disposedValue;
29
30         private void Clear()
31         {
32             if (_stmt != IntPtr.Zero)
33             {
34                 Interop.Sqlite.Finalize(_stmt);
35                 _stmt = IntPtr.Zero;
36             }
37         }
38
39         private bool Prepare(Sql sql)
40         {
41             Clear();
42             Console.WriteLine("Prepare: " + sql.Command);
43             int ret = Interop.Sqlite.Prepare(_conn.GetHandle(), sql.Command, -1, out _stmt, IntPtr.Zero);
44             if (ret != (int)Interop.Sqlite.ResultCode.SQLITE_OK)
45             {
46                 Console.WriteLine("Prepare: failed " + ret);
47                 return false;
48             }
49
50             foreach (var i in sql.Bindings)
51             {
52                 string key = i.Key;
53                 Object obj = i.Value;
54                 int pos = Interop.Sqlite.GetParameterIndex(_stmt, key);
55                 if (pos == 0)
56                     throw new InvalidOperationException("Invalid binding");
57
58                 // TODO: consider null binding
59                 if (obj == null)
60                     continue;
61
62                 Type type = obj.GetType();
63                 if (typeof(int) == type)
64                 {
65                     Interop.Sqlite.BindInt(_stmt, pos, (int)obj);
66                 }
67                 else if (typeof(bool) == type)
68                 {
69                     bool val = (bool)obj;
70                     Interop.Sqlite.BindInt(_stmt, pos, val ? 1 : 0);
71                 }
72                 else if (typeof(double) == type)
73                 {
74                     Interop.Sqlite.BindDouble(_stmt, pos, (double)obj);
75                 }
76                 else if (typeof(string) == type)
77                 {
78                     Interop.Sqlite.BindText(_stmt, pos, (string)obj, -1, (IntPtr)(-1));
79                 }
80                 else if (typeof(byte[]) == type)
81                 {
82                     Interop.Sqlite.BindData(_stmt, pos, (byte[])obj, ((byte[])obj).Length, IntPtr.Zero);
83                 }
84             }
85
86             return true;
87         }
88
89         internal Statement(Connection conn)
90         {
91             _conn = conn;
92         }
93
94         public bool Execute(Sql sql)
95         {
96             if (!Prepare(sql))
97                 return false;
98             int ret = Interop.Sqlite.Step(_stmt);
99             Console.WriteLine("Execute: " + ret); 
100             if (ret != (int)Interop.Sqlite.ResultCode.SQLITE_ROW &&
101                 ret != (int)Interop.Sqlite.ResultCode.SQLITE_DONE)
102                 return false;
103             return true;
104         }
105
106         public int ExecuteUpdate(Sql sql)
107         {
108             if (!Prepare(sql))
109                 return 0;
110             int ret = Interop.Sqlite.Step(_stmt);
111             if (ret != (int)Interop.Sqlite.ResultCode.SQLITE_ROW &&
112                 ret != (int)Interop.Sqlite.ResultCode.SQLITE_DONE)
113                 return 0;
114             return Interop.Sqlite.Changes(_conn.GetHandle());
115         }
116
117         public IResultSet ExecuteQuery(Sql sql)
118         {
119             bool prepared = Prepare(sql);
120
121             if (!prepared)
122                 throw new InvalidOperationException("Couldn't prepare");
123
124             var set = new ResultSet(_stmt, _conn);
125             _stmt = IntPtr.Zero;
126
127             return set;
128         }
129
130         public async Task<IResultSet> ExecuteQueryAsync(Sql sql)
131         {
132             return await Task.Run(() =>
133             {
134                 return ExecuteQuery(sql);
135             }).ConfigureAwait(false);
136         }
137
138         public async Task<int> ExecuteUpdateAsync(Sql sql)
139         {
140             return await Task.Run(() =>
141             {
142                 return ExecuteUpdate(sql);
143             }).ConfigureAwait(false);
144         }
145
146         public async Task<bool> ExecuteAsync(Sql sql)
147         {
148             return await Task.Run(() =>
149             {
150                 return Execute(sql);
151             }).ConfigureAwait(false);
152         }
153
154         protected virtual void Dispose(bool disposing)
155         {
156             if (!disposedValue)
157             {
158                 if (disposing)
159                 {
160                 }
161
162                 Clear();
163                 disposedValue = true;
164             }
165         }
166
167         ~Statement()
168         {
169             Dispose(disposing: false);
170         }
171
172         public void Dispose()
173         {
174             Dispose(disposing: true);
175             GC.SuppressFinalize(this);
176         }
177     }
178 }