Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Merge updates from trunk, including URI file name support. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | stress |
Files: | files | file ages | folders |
SHA1: |
1f317c2618e0db77bbed770fa44e0db6 |
User & Date: | mistachkin 2012-07-11 06:54:56.100 |
Context
2012-07-11
| ||
07:17 | More refinements to stress test. check-in: 3622f106ea user: mistachkin tags: stress | |
06:54 | Merge updates from trunk, including URI file name support. check-in: 1f317c2618 user: mistachkin tags: stress | |
06:53 | In the stress test, open new connections for backup operations. check-in: 125ad3db32 user: mistachkin tags: stress | |
06:47 | Add support for URI file names. check-in: cf96d6650f user: mistachkin tags: trunk | |
Changes
Changes to SQLite.Interop/props/sqlite3.props.
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?xml version="1.0" encoding="utf-8"?> <!-- * * sqlite3.props - * * Written by Joe Mistachkin. * Released to the public domain, use at your own risk! * --> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> <PropertyGroup Label="UserMacros"> <SQLITE_MANIFEST_VERSION>3.7.14</SQLITE_MANIFEST_VERSION> <SQLITE_RC_VERSION>3,7,14</SQLITE_RC_VERSION> | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <?xml version="1.0" encoding="utf-8"?> <!-- * * sqlite3.props - * * Written by Joe Mistachkin. * Released to the public domain, use at your own risk! * --> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> <PropertyGroup Label="UserMacros"> <SQLITE_MANIFEST_VERSION>3.7.14</SQLITE_MANIFEST_VERSION> <SQLITE_RC_VERSION>3,7,14</SQLITE_RC_VERSION> <SQLITE_COMMON_DEFINES>SQLITE_THREADSAFE=1;SQLITE_USE_URI=1;SQLITE_ENABLE_COLUMN_METADATA=1;SQLITE_ENABLE_STAT3=1;SQLITE_ENABLE_FTS3=1;SQLITE_ENABLE_LOAD_EXTENSION=1;SQLITE_ENABLE_RTREE=1;SQLITE_SOUNDEX=1</SQLITE_COMMON_DEFINES> <SQLITE_EXTRA_DEFINES>SQLITE_HAS_CODEC=1</SQLITE_EXTRA_DEFINES> <SQLITE_WINCE_DEFINES>SQLITE_OMIT_WAL=1</SQLITE_WINCE_DEFINES> <SQLITE_DEBUG_DEFINES>SQLITE_DEBUG=1;SQLITE_MEMDEBUG=1</SQLITE_DEBUG_DEFINES> <SQLITE_RELEASE_DEFINES>SQLITE_WIN32_MALLOC=1</SQLITE_RELEASE_DEFINES> <SQLITE_DISABLE_WARNINGS>4018;4055;4057;4090;4100;4127;4132;4146;4152;4210;4232;4244;4245;4389;4701;4706;4996</SQLITE_DISABLE_WARNINGS> <SQLITE_DISABLE_X64_WARNINGS>4232;4267;4306</SQLITE_DISABLE_X64_WARNINGS> </PropertyGroup> |
︙ | ︙ |
Changes to SQLite.Interop/props/sqlite3.vsprops.
︙ | ︙ | |||
20 21 22 23 24 25 26 | <UserMacro Name="SQLITE_RC_VERSION" Value="3,7,14" PerformEnvironmentSet="true" /> <UserMacro Name="SQLITE_COMMON_DEFINES" | | | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <UserMacro Name="SQLITE_RC_VERSION" Value="3,7,14" PerformEnvironmentSet="true" /> <UserMacro Name="SQLITE_COMMON_DEFINES" Value="SQLITE_THREADSAFE=1;SQLITE_USE_URI=1;SQLITE_ENABLE_COLUMN_METADATA=1;SQLITE_ENABLE_STAT3=1;SQLITE_ENABLE_FTS3=1;SQLITE_ENABLE_LOAD_EXTENSION=1;SQLITE_ENABLE_RTREE=1;SQLITE_SOUNDEX=1" PerformEnvironmentSet="true" /> <UserMacro Name="SQLITE_EXTRA_DEFINES" Value="SQLITE_HAS_CODEC=1" PerformEnvironmentSet="true" /> |
︙ | ︙ |
Changes to SQLite.Interop/src/win/interop.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // Additional open flags, we use this one privately //#define SQLITE_OPEN_SHAREDCACHE 0x01000000 typedef void (*SQLITEUSERFUNC)(sqlite3_context *, int, sqlite3_value **); typedef void (*SQLITEFUNCFINAL)(sqlite3_context *); SQLITE_PRIVATE void * sqlite3DbMallocZero_interop(sqlite3 *db, int n) { void *p; if (db) { sqlite3_mutex_enter(db->mutex); } p = sqlite3DbMallocZero(db,n); | > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // Additional open flags, we use this one privately //#define SQLITE_OPEN_SHAREDCACHE 0x01000000 typedef void (*SQLITEUSERFUNC)(sqlite3_context *, int, sqlite3_value **); typedef void (*SQLITEFUNCFINAL)(sqlite3_context *); #if SQLITE_VERSION_NUMBER < 3007014 SQLITE_PRIVATE void * sqlite3DbMallocZero_interop(sqlite3 *db, int n) { void *p; if (db) { sqlite3_mutex_enter(db->mutex); } p = sqlite3DbMallocZero(db,n); |
︙ | ︙ | |||
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | sqlite3MemdebugSetType(p, MEMTYPE_DB|MEMTYPE_HEAP); } sqlite3DbFree(db,p); if (db) { sqlite3_mutex_leave(db->mutex); } } /* The goal of this version of close is different than that of sqlite3_close(), and is designed to lend itself better to .NET's non-deterministic finalizers and the GC thread. SQLite will not close a database if statements are open on it -- but for our purposes, we'd rather finalize all active statements and forcibly close the database. The reason is simple -- a lot of people don't Dispose() of their objects correctly and let the garbage collector do it. This leads to unexpected behavior when a user thinks they've closed a database, but it's still open because not all the statements have hit the GC yet. So, here we have a problem ... .NET has a pointer to any number of sqlite3_stmt objects. We can't call sqlite3_finalize() on these because their memory is freed and can be used for something else. The GC thread could potentially try and call finalize again on the statement after that memory was deallocated. BAD. So, what we need to do is make a copy of each statement, and call finalize() on the copy -- so that the original statement's memory is preserved, and marked as BAD, but we can still manage to finalize everything and forcibly close the database. Later when the GC gets around to calling finalize_interop() on the "bad" statement, we detect that and finish deallocating the pointer. */ SQLITE_API int WINAPI sqlite3_close_interop(sqlite3 *db) { | > < < | | | 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 | sqlite3MemdebugSetType(p, MEMTYPE_DB|MEMTYPE_HEAP); } sqlite3DbFree(db,p); if (db) { sqlite3_mutex_leave(db->mutex); } } #endif /* The goal of this version of close is different than that of sqlite3_close(), and is designed to lend itself better to .NET's non-deterministic finalizers and the GC thread. SQLite will not close a database if statements are open on it -- but for our purposes, we'd rather finalize all active statements and forcibly close the database. The reason is simple -- a lot of people don't Dispose() of their objects correctly and let the garbage collector do it. This leads to unexpected behavior when a user thinks they've closed a database, but it's still open because not all the statements have hit the GC yet. So, here we have a problem ... .NET has a pointer to any number of sqlite3_stmt objects. We can't call sqlite3_finalize() on these because their memory is freed and can be used for something else. The GC thread could potentially try and call finalize again on the statement after that memory was deallocated. BAD. So, what we need to do is make a copy of each statement, and call finalize() on the copy -- so that the original statement's memory is preserved, and marked as BAD, but we can still manage to finalize everything and forcibly close the database. Later when the GC gets around to calling finalize_interop() on the "bad" statement, we detect that and finish deallocating the pointer. */ SQLITE_API int WINAPI sqlite3_close_interop(sqlite3 *db) { #if SQLITE_VERSION_NUMBER >= 3007014 return sqlite3_close_v2(db); #else int ret = sqlite3_close(db); if (ret == SQLITE_BUSY) { sqlite3_mutex_enter(db->mutex); if (!db->pVdbe) { |
︙ | ︙ | |||
112 113 114 115 116 117 118 | ZeroMemory(po, sizeof(Vdbe)); po->magic = VDBE_MAGIC_DEAD; } } sqlite3_mutex_leave(db->mutex); ret = sqlite3_close(db); } | < < > | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | ZeroMemory(po, sizeof(Vdbe)); po->magic = VDBE_MAGIC_DEAD; } } sqlite3_mutex_leave(db->mutex); ret = sqlite3_close(db); } return ret; #endif } SQLITE_API int WINAPI sqlite3_open_interop(const char*filename, int flags, sqlite3 **ppdb) { int ret; //int sharedcache = ((flags & SQLITE_OPEN_SHAREDCACHE) != 0); //flags &= ~SQLITE_OPEN_SHAREDCACHE; |
︙ | ︙ | |||
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 | return ret; #endif } SQLITE_API int WINAPI sqlite3_reset_interop(sqlite3_stmt *stmt) { int ret; if (((Vdbe *)stmt)->magic == VDBE_MAGIC_DEAD) return SQLITE_SCHEMA; ret = sqlite3_reset(stmt); return ret; } SQLITE_API int WINAPI sqlite3_create_function_interop(sqlite3 *psql, const char *zFunctionName, int nArg, int eTextRep, void *pvUser, SQLITEUSERFUNC func, SQLITEUSERFUNC funcstep, SQLITEFUNCFINAL funcfinal, int needCollSeq) { int n; if (eTextRep == SQLITE_UTF16) | > > > > | 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 | return ret; #endif } SQLITE_API int WINAPI sqlite3_reset_interop(sqlite3_stmt *stmt) { #if SQLITE_VERSION_NUMBER >= 3007014 return sqlite3_reset(stmt); #else int ret; if (((Vdbe *)stmt)->magic == VDBE_MAGIC_DEAD) return SQLITE_SCHEMA; ret = sqlite3_reset(stmt); return ret; #endif } SQLITE_API int WINAPI sqlite3_create_function_interop(sqlite3 *psql, const char *zFunctionName, int nArg, int eTextRep, void *pvUser, SQLITEUSERFUNC func, SQLITEUSERFUNC funcstep, SQLITEFUNCFINAL funcfinal, int needCollSeq) { int n; if (eTextRep == SQLITE_UTF16) |
︙ | ︙ |
Changes to System.Data.SQLite/SQLiteBase.cs.
︙ | ︙ | |||
486 487 488 489 490 491 492 493 494 495 496 497 498 499 | [Flags] internal enum SQLiteOpenFlagsEnum { None = 0, ReadOnly = 0x01, ReadWrite = 0x02, Create = 0x04, SharedCache = 0x01000000, Default = 0x06, } /// <summary> /// The extra behavioral flags that can be applied to a connection. /// </summary> | > | 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 | [Flags] internal enum SQLiteOpenFlagsEnum { None = 0, ReadOnly = 0x01, ReadWrite = 0x02, Create = 0x04, Uri = 0x40, SharedCache = 0x01000000, Default = 0x06, } /// <summary> /// The extra behavioral flags that can be applied to a connection. /// </summary> |
︙ | ︙ |
Changes to System.Data.SQLite/SQLiteConnection.cs.
︙ | ︙ | |||
968 969 970 971 972 973 974 | string s = connectionString; int n; SortedList<string, string> ls = new SortedList<string, string>(StringComparer.OrdinalIgnoreCase); // First split into semi-colon delimited values. The Split() function of SQLiteBase accounts for and properly // skips semi-colons in quoted strings string[] arParts = SQLiteConvert.Split(s, ';'); | < | < | > | < > | | 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 | string s = connectionString; int n; SortedList<string, string> ls = new SortedList<string, string>(StringComparer.OrdinalIgnoreCase); // First split into semi-colon delimited values. The Split() function of SQLiteBase accounts for and properly // skips semi-colons in quoted strings string[] arParts = SQLiteConvert.Split(s, ';'); int x = arParts.Length; // For each semi-colon piece, split into key and value pairs by the presence of the = sign for (n = 0; n < x; n++) { int indexOf = arParts[n].IndexOf('='); if (indexOf != -1) ls.Add(arParts[n].Substring(0, indexOf), arParts[n].Substring(indexOf + 1)); else throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, "Invalid ConnectionString format for part \"{0}\"", arParts[n])); } return ls; } #if !PLATFORM_COMPACTFRAMEWORK /// <summary> /// Manual distributed transaction enlistment support |
︙ | ︙ | |||
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 | Close(); SortedList<string, string> opts = ParseConnectionString(_connectionString); _flags = (SQLiteConnectionFlags)Enum.Parse(typeof(SQLiteConnectionFlags), FindKey(opts, "Flags", "Default"), true); string fileName; if (Convert.ToInt32(FindKey(opts, "Version", "3"), CultureInfo.InvariantCulture) != 3) throw new NotSupportedException("Only SQLite Version 3 is supported at this time"); fileName = FindKey(opts, "Data Source", ""); if (String.IsNullOrEmpty(fileName)) { fileName = FindKey(opts, "Uri", ""); if (String.IsNullOrEmpty(fileName)) | > > > > | > > > > > | | | | | | | | > > | 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 | Close(); SortedList<string, string> opts = ParseConnectionString(_connectionString); _flags = (SQLiteConnectionFlags)Enum.Parse(typeof(SQLiteConnectionFlags), FindKey(opts, "Flags", "Default"), true); bool fullUri = false; string fileName; if (Convert.ToInt32(FindKey(opts, "Version", "3"), CultureInfo.InvariantCulture) != 3) throw new NotSupportedException("Only SQLite Version 3 is supported at this time"); fileName = FindKey(opts, "Data Source", ""); if (String.IsNullOrEmpty(fileName)) { fileName = FindKey(opts, "Uri", ""); if (String.IsNullOrEmpty(fileName)) { fileName = FindKey(opts, "FullUri", ""); if (String.IsNullOrEmpty(fileName)) throw new ArgumentException("Data Source cannot be empty. Use :memory: to open an in-memory database"); else fullUri = true; } else fileName = MapUriPath(fileName); } if (!fullUri) { if (String.Compare(fileName, ":MEMORY:", StringComparison.OrdinalIgnoreCase) == 0) fileName = ":memory:"; else { #if PLATFORM_COMPACTFRAMEWORK if (fileName.StartsWith("./") || fileName.StartsWith(".\\")) fileName = Path.GetDirectoryName(System.Reflection.Assembly.GetCallingAssembly().GetName().CodeBase) + fileName.Substring(1); #endif fileName = ExpandFileName(fileName); } } try { bool usePooling = (SQLiteConvert.ToBoolean(FindKey(opts, "Pooling", Boolean.FalseString)) == true); int maxPoolSize = Convert.ToInt32(FindKey(opts, "Max Pool Size", "100"), CultureInfo.InvariantCulture); _defaultTimeout = Convert.ToInt32(FindKey(opts, "Default Timeout", "30"), CultureInfo.CurrentCulture); |
︙ | ︙ | |||
1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 | flags &= ~SQLiteOpenFlagsEnum.Create; } else { flags |= SQLiteOpenFlagsEnum.ReadWrite; } _sql.Open(fileName, _flags, flags, maxPoolSize, usePooling); _binaryGuid = (SQLiteConvert.ToBoolean(FindKey(opts, "BinaryGUID", Boolean.TrueString)) == true); #if INTEROP_CODEC string password = FindKey(opts, "Password", null); if (String.IsNullOrEmpty(password) == false) _sql.SetPassword(System.Text.UTF8Encoding.UTF8.GetBytes(password)); else if (_password != null) _sql.SetPassword(_password); _password = null; #endif | > > > > | > > | | 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 | flags &= ~SQLiteOpenFlagsEnum.Create; } else { flags |= SQLiteOpenFlagsEnum.ReadWrite; } if (fullUri) flags |= SQLiteOpenFlagsEnum.Uri; _sql.Open(fileName, _flags, flags, maxPoolSize, usePooling); _binaryGuid = (SQLiteConvert.ToBoolean(FindKey(opts, "BinaryGUID", Boolean.TrueString)) == true); #if INTEROP_CODEC string password = FindKey(opts, "Password", null); if (String.IsNullOrEmpty(password) == false) _sql.SetPassword(System.Text.UTF8Encoding.UTF8.GetBytes(password)); else if (_password != null) _sql.SetPassword(_password); _password = null; #endif if (!fullUri) _dataSource = Path.GetFileNameWithoutExtension(fileName); else _dataSource = fileName; _version++; ConnectionState oldstate = _connectionState; _connectionState = ConnectionState.Open; try { using (SQLiteCommand cmd = CreateCommand()) { string defValue; if (!fullUri && fileName != ":memory:") { defValue = FindKey(opts, "Page Size", "1024"); if (Convert.ToInt32(defValue, CultureInfo.InvariantCulture) != 1024) { cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA page_size={0}", defValue); cmd.ExecuteNonQuery(); } |
︙ | ︙ |
Changes to Tests/backup.eagle.
︙ | ︙ | |||
78 79 80 81 82 83 84 | ############################################################################### for {set i 0} {$i < [llength $params(pages)]} {incr i} { set pages [lindex $params(pages) $i] set callback [lindex $params(callbacks) $i] runTest {test [appendArgs backup-1. $i] {BackupDatabase method} -setup { | | | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | ############################################################################### for {set i 0} {$i < [llength $params(pages)]} {incr i} { set pages [lindex $params(pages) $i] set callback [lindex $params(callbacks) $i] runTest {test [appendArgs backup-1. $i] {BackupDatabase method} -setup { setupDb [set fileName(1) :memory:] "" "" "" "" "" false false memDb setupDb [set fileName(2) [appendArgs backup-1. $i .db]] } -body { set id [object invoke Interpreter.GetActive NextId] set dataSource [file join [getDatabaseDirectory] $fileName(2)] sql execute $memDb { CREATE TABLE t1(x TEXT); |
︙ | ︙ |
Changes to Tests/common.eagle.
︙ | ︙ | |||
470 471 472 473 474 475 476 | eval $command } proc isMemoryDb { fileName } { # # NOTE: Is the specified database file name really an in-memory database? # | | > | | 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 | eval $command } proc isMemoryDb { fileName } { # # NOTE: Is the specified database file name really an in-memory database? # return [expr {$fileName eq ":memory:" || \ [string range $fileName 0 12] eq "file::memory:"}] } proc setupDb { fileName {mode ""} {dateTimeFormat ""} {dateTimeKind ""} {flags ""} {extra ""} {delete true} {uri false} {varName db} } { # # NOTE: First, see if the caller has requested an in-memory database. # set isMemory [isMemoryDb $fileName] # # NOTE: For now, all test databases used by the test suite are placed into |
︙ | ︙ | |||
517 518 519 520 521 522 523 | # NOTE: Refer to the specified variable (e.g. "db") in the context of the # caller. The handle to the opened database will be stored there. # upvar 1 $varName db # # NOTE: Start building the connection string. The only required portion | | > > > > > > | > | 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 | # NOTE: Refer to the specified variable (e.g. "db") in the context of the # caller. The handle to the opened database will be stored there. # upvar 1 $varName db # # NOTE: Start building the connection string. The only required portion # of the connection string is the data source, which contains the # database file name itself. If the caller wants to use a URI as # the data source, use the FullUri connection string property to # prevent the data source string from being mangled. # if {$uri} then { set connection {FullUri=${fileName}} } else { set connection {Data Source=${fileName}} } # # NOTE: If the caller specified a journal mode, add the necessary portion # of the connection string now. # if {[string length $mode] > 0} then { append connection {;Journal Mode=${mode}} |
︙ | ︙ |