-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSQLite3_UTF16.cs
executable file
·384 lines (336 loc) · 13 KB
/
SQLite3_UTF16.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
/********************************************************
* ADO.NET 2.0 Data Provider for SQLite Version 3.X
* Written by Robert Simpson ([email protected])
*
* Released to the public domain, use at your own risk!
********************************************************/
namespace System.Data.SQLite
{
using System;
using System.Collections.Generic;
#if !NET_COMPACT_20 && TRACE_CONNECTION
using System.Diagnostics;
#endif
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
/// <summary>
/// Alternate SQLite3 object, overriding many text behaviors to support UTF-16 (Unicode)
/// </summary>
internal sealed class SQLite3_UTF16 : SQLite3
{
/// <summary>
/// Constructs the object used to interact with the SQLite core library
/// using the UTF-8 text encoding.
/// </summary>
/// <param name="fmt">
/// The DateTime format to be used when converting string values to a
/// DateTime and binding DateTime parameters.
/// </param>
/// <param name="kind">
/// The <see cref="DateTimeKind" /> to be used when creating DateTime
/// values.
/// </param>
/// <param name="fmtString">
/// The format string to be used when parsing and formatting DateTime
/// values.
/// </param>
/// <param name="db">
/// The native handle to be associated with the database connection.
/// </param>
/// <param name="fileName">
/// The fully qualified file name associated with <paramref name="db" />.
/// </param>
/// <param name="ownHandle">
/// Non-zero if the newly created object instance will need to dispose
/// of <paramref name="db" /> when it is no longer needed.
/// </param>
internal SQLite3_UTF16(
SQLiteDateFormats fmt,
DateTimeKind kind,
string fmtString,
IntPtr db,
string fileName,
bool ownHandle
)
: base(fmt, kind, fmtString, db, fileName, ownHandle)
{
}
///////////////////////////////////////////////////////////////////////////////////////////////
#region IDisposable "Pattern" Members
private bool disposed;
private void CheckDisposed() /* throw */
{
#if THROW_ON_DISPOSED
if (disposed)
throw new ObjectDisposedException(typeof(SQLite3_UTF16).Name);
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////
protected override void Dispose(bool disposing)
{
try
{
if (!disposed)
{
//if (disposing)
//{
// ////////////////////////////////////
// // dispose managed resources here...
// ////////////////////////////////////
//}
//////////////////////////////////////
// release unmanaged resources here...
//////////////////////////////////////
}
}
finally
{
base.Dispose(disposing);
//
// NOTE: Everything should be fully disposed at this point.
//
disposed = true;
}
}
#endregion
///////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Overrides SQLiteConvert.ToString() to marshal UTF-16 strings instead of UTF-8
/// </summary>
/// <param name="b">A pointer to a UTF-16 string</param>
/// <param name="nbytelen">The length (IN BYTES) of the string</param>
/// <returns>A .NET string</returns>
public override string ToString(IntPtr b, int nbytelen)
{
CheckDisposed();
return UTF16ToString(b, nbytelen);
}
public static string UTF16ToString(IntPtr b, int nbytelen)
{
if (nbytelen == 0 || b == IntPtr.Zero) return String.Empty;
if (nbytelen == -1)
return Marshal.PtrToStringUni(b);
else
return Marshal.PtrToStringUni(b, nbytelen / 2);
}
internal override void Open(string strFilename, string vfsName, SQLiteConnectionFlags connectionFlags, SQLiteOpenFlagsEnum openFlags, int maxPoolSize, bool usePool)
{
//
// NOTE: If the database connection is currently open, attempt to
// close it now. This must be done because the file name or
// other parameters that may impact the underlying database
// connection may have changed.
//
if (_sql != null) Close(true);
//
// NOTE: If the connection was not closed successfully, throw an
// exception now.
//
if (_sql != null)
throw new SQLiteException("connection handle is still active");
_usePool = usePool;
_fileName = strFilename;
_flags = connectionFlags;
if (usePool)
{
_sql = SQLiteConnectionPool.Remove(strFilename, maxPoolSize, out _poolVersion);
SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
SQLiteConnectionEventType.OpenedFromPool, null, null,
null, null, _sql, strFilename, new object[] {
typeof(SQLite3_UTF16), strFilename, vfsName,
connectionFlags, openFlags, maxPoolSize, usePool,
_poolVersion }));
#if !NET_COMPACT_20 && TRACE_CONNECTION
Trace.WriteLine(HelperMethods.StringFormat(
CultureInfo.CurrentCulture,
"Open16 (Pool): {0}",
HandleToString()));
#endif
}
if (_sql == null)
{
try
{
// do nothing.
}
finally /* NOTE: Thread.Abort() protection. */
{
IntPtr db = IntPtr.Zero;
SQLiteErrorCode n;
int extFuncs = ((connectionFlags & SQLiteConnectionFlags.NoExtensionFunctions) != SQLiteConnectionFlags.NoExtensionFunctions) ? 1 : 0;
#if !SQLITE_STANDARD
if ((vfsName != null) || (extFuncs != 0))
{
n = UnsafeNativeMethods.sqlite3_open16_interop(ToUTF8(strFilename), ToUTF8(vfsName), openFlags, extFuncs, ref db);
}
else
#endif
{
//
// NOTE: This flag check is designed to enforce the constraint that opening
// a database file that does not already exist requires specifying the
// "Create" flag, even when a native API is used that does not accept
// a flags parameter.
//
if (((openFlags & SQLiteOpenFlagsEnum.Create) != SQLiteOpenFlagsEnum.Create) && !File.Exists(strFilename))
throw new SQLiteException(SQLiteErrorCode.CantOpen, strFilename);
if (vfsName != null)
{
throw new SQLiteException(SQLiteErrorCode.CantOpen, HelperMethods.StringFormat(
CultureInfo.CurrentCulture,
"cannot open using UTF-16 and VFS \"{0}\": need interop assembly", vfsName));
}
n = UnsafeNativeMethods.sqlite3_open16(strFilename, ref db);
}
#if !NET_COMPACT_20 && TRACE_CONNECTION
Trace.WriteLine(HelperMethods.StringFormat(
CultureInfo.CurrentCulture,
"Open16: {0}", db));
#endif
if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, null);
_sql = new SQLiteConnectionHandle(db, true);
}
lock (_sql) { /* HACK: Force the SyncBlock to be "created" now. */ }
SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
SQLiteConnectionEventType.NewCriticalHandle, null,
null, null, null, _sql, strFilename, new object[] {
typeof(SQLite3_UTF16), strFilename, vfsName,
connectionFlags, openFlags, maxPoolSize, usePool }));
}
// Bind functions to this connection. If any previous functions of the same name
// were already bound, then the new bindings replace the old.
if ((connectionFlags & SQLiteConnectionFlags.NoBindFunctions) != SQLiteConnectionFlags.NoBindFunctions)
{
if (_functions == null)
_functions = new Dictionary<SQLiteFunctionAttribute, SQLiteFunction>();
foreach (KeyValuePair<SQLiteFunctionAttribute, SQLiteFunction> pair
in SQLiteFunction.BindFunctions(this, connectionFlags))
{
_functions[pair.Key] = pair.Value;
}
}
SetTimeout(0);
GC.KeepAlive(_sql);
}
internal override void Bind_DateTime(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, DateTime dt)
{
switch (_datetimeFormat)
{
case SQLiteDateFormats.Ticks:
case SQLiteDateFormats.JulianDay:
case SQLiteDateFormats.UnixEpoch:
{
base.Bind_DateTime(stmt, flags, index, dt);
break;
}
default:
{
#if !PLATFORM_COMPACTFRAMEWORK
if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind)
{
SQLiteStatementHandle handle =
(stmt != null) ? stmt._sqlite_stmt : null;
LogBind(handle, index, dt);
}
#endif
Bind_Text(stmt, flags, index, ToString(dt));
break;
}
}
}
internal override void Bind_Text(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, string value)
{
SQLiteStatementHandle handle = stmt._sqlite_stmt;
#if !PLATFORM_COMPACTFRAMEWORK
if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind)
{
LogBind(handle, index, value);
}
#endif
SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_text16(handle, index, value, value.Length * 2, (IntPtr)(-1));
if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
}
internal override DateTime GetDateTime(SQLiteStatement stmt, int index)
{
if (_datetimeFormat == SQLiteDateFormats.Ticks)
return TicksToDateTime(GetInt64(stmt, index), _datetimeKind);
else if (_datetimeFormat == SQLiteDateFormats.JulianDay)
return ToDateTime(GetDouble(stmt, index), _datetimeKind);
else if (_datetimeFormat == SQLiteDateFormats.UnixEpoch)
return UnixEpochToDateTime(GetInt64(stmt, index), _datetimeKind);
return ToDateTime(GetText(stmt, index));
}
internal override string ColumnName(SQLiteStatement stmt, int index)
{
#if !SQLITE_STANDARD
int len = 0;
IntPtr p = UnsafeNativeMethods.sqlite3_column_name16_interop(stmt._sqlite_stmt, index, ref len);
#else
IntPtr p = UnsafeNativeMethods.sqlite3_column_name16(stmt._sqlite_stmt, index);
#endif
if (p == IntPtr.Zero)
throw new SQLiteException(SQLiteErrorCode.NoMem, GetLastError());
#if !SQLITE_STANDARD
return UTF16ToString(p, len);
#else
return UTF16ToString(p, -1);
#endif
}
internal override string GetText(SQLiteStatement stmt, int index)
{
#if !SQLITE_STANDARD
int len = 0;
return UTF16ToString(UnsafeNativeMethods.sqlite3_column_text16_interop(stmt._sqlite_stmt, index, ref len), len);
#else
return UTF16ToString(UnsafeNativeMethods.sqlite3_column_text16(stmt._sqlite_stmt, index),
UnsafeNativeMethods.sqlite3_column_bytes16(stmt._sqlite_stmt, index));
#endif
}
internal override string ColumnOriginalName(SQLiteStatement stmt, int index)
{
#if !SQLITE_STANDARD
int len = 0;
return UTF16ToString(UnsafeNativeMethods.sqlite3_column_origin_name16_interop(stmt._sqlite_stmt, index, ref len), len);
#else
return UTF16ToString(UnsafeNativeMethods.sqlite3_column_origin_name16(stmt._sqlite_stmt, index), -1);
#endif
}
internal override string ColumnDatabaseName(SQLiteStatement stmt, int index)
{
#if !SQLITE_STANDARD
int len = 0;
return UTF16ToString(UnsafeNativeMethods.sqlite3_column_database_name16_interop(stmt._sqlite_stmt, index, ref len), len);
#else
return UTF16ToString(UnsafeNativeMethods.sqlite3_column_database_name16(stmt._sqlite_stmt, index), -1);
#endif
}
internal override string ColumnTableName(SQLiteStatement stmt, int index)
{
#if !SQLITE_STANDARD
int len = 0;
return UTF16ToString(UnsafeNativeMethods.sqlite3_column_table_name16_interop(stmt._sqlite_stmt, index, ref len), len);
#else
return UTF16ToString(UnsafeNativeMethods.sqlite3_column_table_name16(stmt._sqlite_stmt, index), -1);
#endif
}
internal override string GetParamValueText(IntPtr ptr)
{
#if !SQLITE_STANDARD
int len = 0;
return UTF16ToString(UnsafeNativeMethods.sqlite3_value_text16_interop(ptr, ref len), len);
#else
return UTF16ToString(UnsafeNativeMethods.sqlite3_value_text16(ptr),
UnsafeNativeMethods.sqlite3_value_bytes16(ptr));
#endif
}
internal override void ReturnError(IntPtr context, string value)
{
UnsafeNativeMethods.sqlite3_result_error16(context, value, value.Length * 2);
}
internal override void ReturnText(IntPtr context, string value)
{
UnsafeNativeMethods.sqlite3_result_text16(context, value, value.Length * 2, (IntPtr)(-1));
}
}
}