Index: System.Data.SQLite/SQLite3.cs
==================================================================
--- System.Data.SQLite/SQLite3.cs
+++ System.Data.SQLite/SQLite3.cs
@@ -38,12 +38,12 @@
///
/// The user-defined functions registered on this connection
///
protected SQLiteFunction[] _functionsArray;
- internal SQLite3(SQLiteDateFormats fmt)
- : base(fmt)
+ internal SQLite3(SQLiteDateFormats fmt, DateTimeKind kind)
+ : base(fmt, kind)
{
}
protected override void Dispose(bool bDisposing)
{
@@ -262,10 +262,30 @@
return SQLiteBase.SQLiteLastError(_sql);
}
internal override SQLiteStatement Prepare(SQLiteConnection cnn, string strSql, SQLiteStatement previous, uint timeoutMS, out string strRemain)
{
+ if (!String.IsNullOrEmpty(strSql))
+ {
+ //
+ // NOTE: SQLite does not support the concept of separate schemas
+ // in one database; therefore, remove the base schema name
+ // used to smooth integration with the base .NET Framework
+ // data classes.
+ //
+ string baseSchemaName = (cnn != null) ? cnn._baseSchemaName : null;
+
+ if (!String.IsNullOrEmpty(baseSchemaName))
+ {
+ strSql = strSql.Replace(
+ String.Format("[{0}].", baseSchemaName), String.Empty);
+
+ strSql = strSql.Replace(
+ String.Format("{0}.", baseSchemaName), String.Empty);
+ }
+ }
+
IntPtr stmt = IntPtr.Zero;
IntPtr ptr = IntPtr.Zero;
int len = 0;
int n = 17;
int retries = 0;
@@ -406,13 +426,38 @@
if (n > 0) throw new SQLiteException(n, SQLiteLastError());
}
internal override void Bind_DateTime(SQLiteStatement stmt, int index, DateTime dt)
{
- byte[] b = ToUTF8(dt);
- int n = UnsafeNativeMethods.sqlite3_bind_text(stmt._sqlite_stmt, index, b, b.Length - 1, (IntPtr)(-1));
- if (n > 0) throw new SQLiteException(n, SQLiteLastError());
+ switch (_datetimeFormat)
+ {
+ case SQLiteDateFormats.Ticks:
+ {
+ int n = UnsafeNativeMethods.sqlite3_bind_int64(stmt._sqlite_stmt, index, dt.Ticks);
+ if (n > 0) throw new SQLiteException(n, SQLiteLastError());
+ break;
+ }
+ case SQLiteDateFormats.JulianDay:
+ {
+ int n = UnsafeNativeMethods.sqlite3_bind_double(stmt._sqlite_stmt, index, ToJulianDay(dt));
+ if (n > 0) throw new SQLiteException(n, SQLiteLastError());
+ break;
+ }
+ case SQLiteDateFormats.UnixEpoch:
+ {
+ int n = UnsafeNativeMethods.sqlite3_bind_int64(stmt._sqlite_stmt, index, Convert.ToInt64(dt.Subtract(UnixEpoch).TotalSeconds));
+ if (n > 0) throw new SQLiteException(n, SQLiteLastError());
+ break;
+ }
+ default:
+ {
+ byte[] b = ToUTF8(dt);
+ int n = UnsafeNativeMethods.sqlite3_bind_text(stmt._sqlite_stmt, index, b, b.Length - 1, (IntPtr)(-1));
+ if (n > 0) throw new SQLiteException(n, SQLiteLastError());
+ break;
+ }
+ }
}
internal override void Bind_Blob(SQLiteStatement stmt, int index, byte[] blobData)
{
int n = UnsafeNativeMethods.sqlite3_bind_blob(stmt._sqlite_stmt, index, blobData, blobData.Length, (IntPtr)(-1));
Index: System.Data.SQLite/SQLite3_UTF16.cs
==================================================================
--- System.Data.SQLite/SQLite3_UTF16.cs
+++ System.Data.SQLite/SQLite3_UTF16.cs
@@ -13,12 +13,12 @@
///
/// Alternate SQLite3 object, overriding many text behaviors to support UTF-16 (Unicode)
///
internal class SQLite3_UTF16 : SQLite3
{
- internal SQLite3_UTF16(SQLiteDateFormats fmt)
- : base(fmt)
+ internal SQLite3_UTF16(SQLiteDateFormats fmt, DateTimeKind kind)
+ : base(fmt, kind)
{
}
///
/// Overrides SQLiteConvert.ToString() to marshal UTF-16 strings instead of UTF-8
@@ -70,12 +70,37 @@
}
_functionsArray = SQLiteFunction.BindFunctions(this);
}
internal override void Bind_DateTime(SQLiteStatement stmt, int index, DateTime dt)
- {
- Bind_Text(stmt, index, ToString(dt));
+ {
+ switch (_datetimeFormat)
+ {
+ case SQLiteDateFormats.Ticks:
+ {
+ int n = UnsafeNativeMethods.sqlite3_bind_int64(stmt._sqlite_stmt, index, dt.Ticks);
+ if (n > 0) throw new SQLiteException(n, SQLiteLastError());
+ break;
+ }
+ case SQLiteDateFormats.JulianDay:
+ {
+ int n = UnsafeNativeMethods.sqlite3_bind_double(stmt._sqlite_stmt, index, ToJulianDay(dt));
+ if (n > 0) throw new SQLiteException(n, SQLiteLastError());
+ break;
+ }
+ case SQLiteDateFormats.UnixEpoch:
+ {
+ int n = UnsafeNativeMethods.sqlite3_bind_int64(stmt._sqlite_stmt, index, Convert.ToInt64(dt.Subtract(UnixEpoch).TotalSeconds));
+ if (n > 0) throw new SQLiteException(n, SQLiteLastError());
+ break;
+ }
+ default:
+ {
+ Bind_Text(stmt, index, ToString(dt));
+ break;
+ }
+ }
}
internal override void Bind_Text(SQLiteStatement stmt, int index, string value)
{
int n = UnsafeNativeMethods.sqlite3_bind_text16(stmt._sqlite_stmt, index, value, value.Length * 2, (IntPtr)(-1));
Index: System.Data.SQLite/SQLiteBase.cs
==================================================================
--- System.Data.SQLite/SQLiteBase.cs
+++ System.Data.SQLite/SQLiteBase.cs
@@ -13,12 +13,12 @@
/// This internal class provides the foundation of SQLite support. It defines all the abstract members needed to implement
/// a SQLite data provider, and inherits from SQLiteConvert which allows for simple translations of string to and from SQLite.
///
internal abstract class SQLiteBase : SQLiteConvert, IDisposable
{
- internal SQLiteBase(SQLiteDateFormats fmt)
- : base(fmt) { }
+ internal SQLiteBase(SQLiteDateFormats fmt, DateTimeKind kind)
+ : base(fmt, kind) { }
static internal object _lock = new object();
///
/// Returns a string representing the active version of SQLite
Index: System.Data.SQLite/SQLiteConnection.cs
==================================================================
--- System.Data.SQLite/SQLiteConnection.cs
+++ System.Data.SQLite/SQLiteConnection.cs
@@ -50,10 +50,25 @@
/// DateTimeFormat
/// Ticks - Use DateTime.Ticks
ISO8601 - Use ISO8601 DateTime format
/// N
/// ISO8601
///
+ /// -
+ /// DateTimeKind
+ /// Unspecified - Not specified as either UTC or local time.
Utc - The time represented is UTC.
Local - The time represented is local time.
+ /// N
+ /// Unspecified
+ ///
+ /// -
+ /// BaseSchemaName
+ /// Some base data classes in the framework (e.g. those that build SQL queries dynamically)
+ /// assume that an ADO.NET provider cannot support an alternate catalog (i.e. database) without supporting
+ /// alternate schemas as well; however, SQLite does not fit into this model. Therefore, this value is used
+ /// as a placeholder and removed prior to preparing any SQL statements that may contain it.
+ /// N
+ /// sqlite_default_schema
+ ///
/// -
/// BinaryGUID
/// True - Store GUID columns in binary form
False - Store GUID columns as text
/// N
/// True
@@ -150,10 +165,19 @@
///
///
///
public sealed partial class SQLiteConnection : DbConnection, ICloneable
{
+ ///
+ /// The default "stub" (i.e. placeholder) base schema name to use when
+ /// returning column schema information. Used as the initial value of
+ /// the BaseSchemaName property. This should start with "sqlite_*"
+ /// because those names are reserved for use by SQLite (i.e. they cannot
+ /// be confused with the names of user objects).
+ ///
+ private const string DefaultBaseSchemaName = "sqlite_default_schema";
+
private const int SQLITE_FCNTL_WIN32_AV_RETRY = 9;
private const string _dataDirectory = "|DataDirectory|";
private const string _masterdb = "sqlite_master";
private const string _tempmasterdb = "sqlite_temp_master";
@@ -193,10 +217,16 @@
///
/// Temporary password storage, emptied after the database has been opened
///
private byte[] _password;
+ ///
+ /// The "stub" (i.e. placeholder) base schema name to use when returning
+ /// column schema information.
+ ///
+ internal string _baseSchemaName;
+
///
/// Default command timeout
///
private int _defaultTimeout = 30;
@@ -803,10 +833,12 @@
_defaultIsolation = (IsolationLevel)Enum.Parse(typeof(IsolationLevel), FindKey(opts, "Default IsolationLevel", "Serializable"), true);
if (_defaultIsolation != IsolationLevel.Serializable && _defaultIsolation != IsolationLevel.ReadCommitted)
throw new NotSupportedException("Invalid Default IsolationLevel specified");
+ _baseSchemaName = FindKey(opts, "BaseSchemaName", DefaultBaseSchemaName);
+
//string temp = FindKey(opts, "DateTimeFormat", "ISO8601");
//if (String.Compare(temp, "ticks", StringComparison.OrdinalIgnoreCase) == 0) dateFormat = SQLiteDateFormats.Ticks;
//else if (String.Compare(temp, "julianday", StringComparison.OrdinalIgnoreCase) == 0) dateFormat = SQLiteDateFormats.JulianDay;
if (_sql == null)
@@ -813,15 +845,18 @@
{
bool bUTF16 = (SQLiteConvert.ToBoolean(FindKey(opts, "UseUTF16Encoding", Boolean.FalseString)) == true);
SQLiteDateFormats dateFormat = (SQLiteDateFormats)Enum.Parse(typeof(SQLiteDateFormats),
FindKey(opts, "DateTimeFormat", "ISO8601"),
true);
+
+ DateTimeKind kind = (DateTimeKind)Enum.Parse(typeof(DateTimeKind),
+ FindKey(opts, "DateTimeKind", "Unspecified"), true);
if (bUTF16) // SQLite automatically sets the encoding of the database to UTF16 if called from sqlite3_open16()
- _sql = new SQLite3_UTF16(dateFormat);
+ _sql = new SQLite3_UTF16(dateFormat, kind);
else
- _sql = new SQLite3(dateFormat);
+ _sql = new SQLite3(dateFormat, kind);
}
SQLiteOpenFlagsEnum flags = SQLiteOpenFlagsEnum.None;
if (SQLiteConvert.ToBoolean(FindKey(opts, "FailIfMissing", Boolean.FalseString)) == false)
@@ -1035,15 +1070,18 @@
bool bUTF16 = (SQLiteConvert.ToBoolean(FindKey(opts, "UseUTF16Encoding", Boolean.FalseString)) == true);
SQLiteDateFormats dateFormat = (SQLiteDateFormats)Enum.Parse(typeof(SQLiteDateFormats),
FindKey(opts, "DateTimeFormat", "ISO8601"),
true);
+
+ DateTimeKind kind = (DateTimeKind)Enum.Parse(typeof(DateTimeKind),
+ FindKey(opts, "DateTimeKind", "Unspecified"), true);
if (bUTF16) // SQLite automatically sets the encoding of the database to UTF16 if called from sqlite3_open16()
- _sql = new SQLite3_UTF16(dateFormat);
+ _sql = new SQLite3_UTF16(dateFormat, kind);
else
- _sql = new SQLite3(dateFormat);
+ _sql = new SQLite3(dateFormat, kind);
}
if (_sql != null) return _sql.Shutdown();
throw new InvalidOperationException("Database connection not active.");
}
Index: System.Data.SQLite/SQLiteConvert.cs
==================================================================
--- System.Data.SQLite/SQLiteConvert.cs
+++ System.Data.SQLite/SQLiteConvert.cs
@@ -19,39 +19,58 @@
public abstract class SQLiteConvert
{
///
/// The value for the Unix epoch (e.g. January 1, 1970 at midnight, in UTC).
///
- private static readonly DateTime UnixEpoch =
+ protected static readonly DateTime UnixEpoch =
new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+ ///
+ /// The value of the OLE Automation epoch represented as a Julian day.
+ ///
+ private static readonly double OleAutomationEpochAsJulianDay = 2415018.5;
+
///
/// The format string for DateTime values when using the InvariantCulture or CurrentCulture formats.
///
private const string FullFormat = "yyyy-MM-ddTHH:mm:ss.fffffffK";
///
/// An array of ISO8601 datetime formats we support conversion from
///
private static string[] _datetimeFormats = new string[] {
+ "THHmmssK",
+ "THHmmK",
+ "HH:mm:ss.FFFFFFFK",
+ "HH:mm:ssK",
+ "HH:mmK",
+ "yyyy-MM-dd HH:mm:ss.FFFFFFFK", /* NOTE: UTC default (5). */
+ "yyyy-MM-dd HH:mm:ssK",
+ "yyyy-MM-dd HH:mmK",
+ "yyyy-MM-ddTHH:mm:ss.FFFFFFFK",
+ "yyyy-MM-ddTHH:mmK",
+ "yyyy-MM-ddTHH:mm:ssK",
+ "yyyyMMddHHmmssK",
+ "yyyyMMddHHmmK",
+ "yyyyMMddTHHmmssFFFFFFFK",
"THHmmss",
"THHmm",
+ "HH:mm:ss.FFFFFFF",
"HH:mm:ss",
"HH:mm",
- "HH:mm:ss.FFFFFFF",
- "yy-MM-dd",
- "yyyy-MM-dd",
- "yyyy-MM-dd HH:mm:ss.FFFFFFF",
+ "yyyy-MM-dd HH:mm:ss.FFFFFFF", /* NOTE: Non-UTC default (19). */
"yyyy-MM-dd HH:mm:ss",
- "yyyy-MM-dd HH:mm",
+ "yyyy-MM-dd HH:mm",
"yyyy-MM-ddTHH:mm:ss.FFFFFFF",
"yyyy-MM-ddTHH:mm",
"yyyy-MM-ddTHH:mm:ss",
"yyyyMMddHHmmss",
"yyyyMMddHHmm",
"yyyyMMddTHHmmssFFFFFFF",
- "yyyyMMdd"
+ "yyyy-MM-dd",
+ "yyyyMMdd",
+ "yy-MM-dd"
};
///
/// An UTF-8 Encoding instance, so we can convert strings to and from UTF-8
///
@@ -59,16 +78,22 @@
///
/// The default DateTime format for this instance
///
internal SQLiteDateFormats _datetimeFormat;
///
+ /// The default DateTimeKind for this instance.
+ ///
+ internal DateTimeKind _datetimeKind;
+ ///
/// Initializes the conversion class
///
/// The default date/time format to use for this instance
- internal SQLiteConvert(SQLiteDateFormats fmt)
+ /// The DateTimeKind to use.
+ internal SQLiteConvert(SQLiteDateFormats fmt, DateTimeKind kind)
{
_datetimeFormat = fmt;
+ _datetimeKind = kind;
}
#region UTF-8 Conversion Functions
///
/// Converts a string to a UTF-8 encoded byte array sized to include a null-terminating character.
@@ -143,61 +168,177 @@
///
/// Converts a string into a DateTime, using the current DateTimeFormat specified for the connection when it was opened.
///
///
/// Acceptable ISO8601 DateTime formats are:
- /// yyyy-MM-dd HH:mm:ss
- /// yyyyMMddHHmmss
- /// yyyyMMddTHHmmssfffffff
- /// yyyy-MM-dd
- /// yy-MM-dd
- /// yyyyMMdd
- /// HH:mm:ss
+ /// THHmmssK
+ /// THHmmK
+ /// HH:mm:ss.FFFFFFFK
+ /// HH:mm:ssK
+ /// HH:mmK
+ /// yyyy-MM-dd HH:mm:ss.FFFFFFFK
+ /// yyyy-MM-dd HH:mm:ssK
+ /// yyyy-MM-dd HH:mmK
+ /// yyyy-MM-ddTHH:mm:ss.FFFFFFFK
+ /// yyyy-MM-ddTHH:mmK
+ /// yyyy-MM-ddTHH:mm:ssK
+ /// yyyyMMddHHmmssK
+ /// yyyyMMddHHmmK
+ /// yyyyMMddTHHmmssFFFFFFFK
/// THHmmss
+ /// THHmm
+ /// HH:mm:ss.FFFFFFF
+ /// HH:mm:ss
+ /// HH:mm
+ /// yyyy-MM-dd HH:mm:ss.FFFFFFF
+ /// yyyy-MM-dd HH:mm:ss
+ /// yyyy-MM-dd HH:mm
+ /// yyyy-MM-ddTHH:mm:ss.FFFFFFF
+ /// yyyy-MM-ddTHH:mm
+ /// yyyy-MM-ddTHH:mm:ss
+ /// yyyyMMddHHmmss
+ /// yyyyMMddHHmm
+ /// yyyyMMddTHHmmssFFFFFFF
+ /// yyyy-MM-dd
+ /// yyyyMMdd
+ /// yy-MM-dd
///
/// The string containing either a long integer number of 100-nanosecond units since
/// System.DateTime.MinValue, a Julian day double, an integer number of seconds since the Unix epoch, a
/// culture-independent formatted date and time string, a formatted date and time string in the current
/// culture, or an ISO8601-format string.
/// A DateTime value
public DateTime ToDateTime(string dateText)
{
- switch (_datetimeFormat)
- {
- case SQLiteDateFormats.Ticks:
- return new DateTime(Convert.ToInt64(dateText, CultureInfo.InvariantCulture));
- case SQLiteDateFormats.JulianDay:
- return ToDateTime(Convert.ToDouble(dateText, CultureInfo.InvariantCulture));
- case SQLiteDateFormats.UnixEpoch:
- return UnixEpoch.AddSeconds(Convert.ToInt32(dateText, CultureInfo.InvariantCulture));
- case SQLiteDateFormats.InvariantCulture:
- return DateTime.Parse(dateText, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None);
- case SQLiteDateFormats.CurrentCulture:
- return DateTime.Parse(dateText, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None);
- default:
- return DateTime.ParseExact(dateText, _datetimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None);
- }
+ return ToDateTime(dateText, _datetimeFormat, _datetimeKind);
+ }
+
+ ///
+ /// Converts a string into a DateTime, using the specified DateTimeFormat and DateTimeKind.
+ ///
+ ///
+ /// Acceptable ISO8601 DateTime formats are:
+ /// THHmmssK
+ /// THHmmK
+ /// HH:mm:ss.FFFFFFFK
+ /// HH:mm:ssK
+ /// HH:mmK
+ /// yyyy-MM-dd HH:mm:ss.FFFFFFFK
+ /// yyyy-MM-dd HH:mm:ssK
+ /// yyyy-MM-dd HH:mmK
+ /// yyyy-MM-ddTHH:mm:ss.FFFFFFFK
+ /// yyyy-MM-ddTHH:mmK
+ /// yyyy-MM-ddTHH:mm:ssK
+ /// yyyyMMddHHmmssK
+ /// yyyyMMddHHmmK
+ /// yyyyMMddTHHmmssFFFFFFFK
+ /// THHmmss
+ /// THHmm
+ /// HH:mm:ss.FFFFFFF
+ /// HH:mm:ss
+ /// HH:mm
+ /// yyyy-MM-dd HH:mm:ss.FFFFFFF
+ /// yyyy-MM-dd HH:mm:ss
+ /// yyyy-MM-dd HH:mm
+ /// yyyy-MM-ddTHH:mm:ss.FFFFFFF
+ /// yyyy-MM-ddTHH:mm
+ /// yyyy-MM-ddTHH:mm:ss
+ /// yyyyMMddHHmmss
+ /// yyyyMMddHHmm
+ /// yyyyMMddTHHmmssFFFFFFF
+ /// yyyy-MM-dd
+ /// yyyyMMdd
+ /// yy-MM-dd
+ ///
+ /// The string containing either a long integer number of 100-nanosecond units since
+ /// System.DateTime.MinValue, a Julian day double, an integer number of seconds since the Unix epoch, a
+ /// culture-independent formatted date and time string, a formatted date and time string in the current
+ /// culture, or an ISO8601-format string.
+ /// The SQLiteDateFormats to use.
+ /// The DateTimeKind to use.
+ /// A DateTime value
+ public DateTime ToDateTime(string dateText, SQLiteDateFormats format, DateTimeKind kind)
+ {
+ switch (format)
+ {
+ case SQLiteDateFormats.Ticks:
+ {
+ return new DateTime(Convert.ToInt64(
+ dateText, CultureInfo.InvariantCulture), kind);
+ }
+ case SQLiteDateFormats.JulianDay:
+ {
+ return ToDateTime(Convert.ToDouble(
+ dateText, CultureInfo.InvariantCulture), kind);
+ }
+ case SQLiteDateFormats.UnixEpoch:
+ {
+ return DateTime.SpecifyKind(
+ UnixEpoch.AddSeconds(Convert.ToInt32(
+ dateText, CultureInfo.InvariantCulture)), kind);
+ }
+ case SQLiteDateFormats.InvariantCulture:
+ {
+ return DateTime.SpecifyKind(DateTime.Parse(
+ dateText, DateTimeFormatInfo.InvariantInfo,
+ kind == DateTimeKind.Utc ?
+ DateTimeStyles.AdjustToUniversal :
+ DateTimeStyles.None),
+ kind);
+ }
+ case SQLiteDateFormats.CurrentCulture:
+ {
+ return DateTime.SpecifyKind(DateTime.Parse(
+ dateText, DateTimeFormatInfo.CurrentInfo,
+ kind == DateTimeKind.Utc ?
+ DateTimeStyles.AdjustToUniversal :
+ DateTimeStyles.None),
+ kind);
+ }
+ default:
+ {
+ return DateTime.SpecifyKind(DateTime.ParseExact(
+ dateText, _datetimeFormats,
+ DateTimeFormatInfo.InvariantInfo,
+ kind == DateTimeKind.Utc ?
+ DateTimeStyles.AdjustToUniversal :
+ DateTimeStyles.None),
+ kind);
+ }
+ }
}
///
/// Converts a julianday value into a DateTime
///
/// The value to convert
/// A .NET DateTime
public DateTime ToDateTime(double julianDay)
{
- return DateTime.FromOADate(julianDay - 2415018.5);
+ return ToDateTime(julianDay, _datetimeKind);
+ }
+
+ ///
+ /// Converts a julianday value into a DateTime
+ ///
+ /// The value to convert
+ /// The DateTimeKind to use.
+ /// A .NET DateTime
+ public DateTime ToDateTime(double julianDay, DateTimeKind kind)
+ {
+ return DateTime.SpecifyKind(
+ DateTime.FromOADate(julianDay - OleAutomationEpochAsJulianDay), kind);
}
///
/// Converts a DateTime struct to a JulianDay double
///
/// The DateTime to convert
/// The JulianDay value the Datetime represents
public double ToJulianDay(DateTime value)
{
- return value.ToOADate() + 2415018.5;
+ return value.ToOADate() + OleAutomationEpochAsJulianDay;
}
///
/// Converts a DateTime to a string value, using the current DateTimeFormat specified for the connection when it was opened.
///
@@ -218,11 +359,13 @@
case SQLiteDateFormats.InvariantCulture:
return dateValue.ToString(FullFormat, CultureInfo.InvariantCulture);
case SQLiteDateFormats.CurrentCulture:
return dateValue.ToString(FullFormat, CultureInfo.CurrentCulture);
default:
- return dateValue.ToString(_datetimeFormats[7], CultureInfo.InvariantCulture);
+ return (dateValue.Kind == DateTimeKind.Utc) ?
+ dateValue.ToString(_datetimeFormats[5], CultureInfo.InvariantCulture) : // include "Z"
+ dateValue.ToString(_datetimeFormats[19], CultureInfo.InvariantCulture);
}
}
///
/// Internal function to convert a UTF-8 encoded IntPtr of the specified length to a DateTime.
Index: System.Data.SQLite/SQLiteDataReader.cs
==================================================================
--- System.Data.SQLite/SQLiteDataReader.cs
+++ System.Data.SQLite/SQLiteDataReader.cs
@@ -14,11 +14,11 @@
///
/// SQLite implementation of DbDataReader.
///
public sealed class SQLiteDataReader : DbDataReader
- {
+ {
///
/// Underlying command this reader is attached to
///
private SQLiteCommand _command;
///
@@ -73,11 +73,21 @@
///
/// An array of rowid's for the active statement if CommandBehavior.KeyInfo is specified
///
private SQLiteKeyReader _keyInfo;
- internal long _version; // Matches the version of the connection
+ ///
+ /// Matches the version of the connection.
+ ///
+ internal long _version;
+
+ ///
+ /// The "stub" (i.e. placeholder) base schema name to use when returning
+ /// column schema information. Matches the base schema name used by the
+ /// associated connection.
+ ///
+ private string _baseSchemaName;
///
/// Internal constructor, initializes the datareader and sets up to begin executing statements
///
/// The SQLiteCommand this data reader is for
@@ -84,11 +94,12 @@
/// The expected behavior of the data reader
internal SQLiteDataReader(SQLiteCommand cmd, CommandBehavior behave)
{
_throwOnDisposed = true;
_command = cmd;
- _version = _command.Connection._version;
+ _version = _command.Connection._version;
+ _baseSchemaName = _command.Connection._baseSchemaName;
_commandBehavior = behave;
_activeStatementIndex = -1;
_rowsAffected = -1;
@@ -637,11 +648,12 @@
row[SchemaTableOptionalColumn.IsRowVersion] = false;
row[SchemaTableColumn.IsUnique] = false;
row[SchemaTableColumn.IsKey] = false;
row[SchemaTableOptionalColumn.IsAutoIncrement] = false;
row[SchemaTableColumn.DataType] = GetFieldType(n);
- row[SchemaTableOptionalColumn.IsHidden] = false;
+ row[SchemaTableOptionalColumn.IsHidden] = false;
+ row[SchemaTableColumn.BaseSchemaName] = _baseSchemaName;
strColumn = _command.Connection._sql.ColumnOriginalName(_activeStatement, n);
if (String.IsNullOrEmpty(strColumn) == false) row[SchemaTableColumn.BaseColumnName] = strColumn;
row[SchemaTableColumn.IsExpression] = String.IsNullOrEmpty(strColumn);
Index: System.Data.SQLite/SQLiteLog.cs
==================================================================
--- System.Data.SQLite/SQLiteLog.cs
+++ System.Data.SQLite/SQLiteLog.cs
@@ -146,11 +146,12 @@
//
// NOTE: Create an instance of the SQLite wrapper class.
//
if (_sql == null)
- _sql = new SQLite3(SQLiteDateFormats.Default);
+ _sql = new SQLite3(SQLiteDateFormats.Default,
+ DateTimeKind.Unspecified);
//
// NOTE: Create a single "global" (i.e. per-process) callback
// to register with SQLite. This callback will pass the
// event on to any registered handler. We only want to
Index: Tests/basic.eagle
==================================================================
--- Tests/basic.eagle
+++ Tests/basic.eagle
@@ -649,11 +649,11 @@
{z 5678}}} 2 {1 {{x 1} {y foobar} {z 1234}}} {2 {{x 2} {y foobar} {z 5678}}}}}
###############################################################################
runTest {test basic-1.12 {DateTime using Unix epoch} -setup {
- setupDb [set fileName basic-1.12.db] "" "" "DateTimeFormat=UnixEpoch;"
+ setupDb [set fileName basic-1.12.db] "" UnixEpoch Utc
} -body {
set result [list]
sql execute $db "CREATE TABLE t1(x INTEGER PRIMARY KEY ASC, y DATETIME);"
sql execute $db "INSERT INTO t1 (x, y) VALUES(1, 1302825600);"
@@ -676,11 +676,11 @@
[list param1 DateTime 1334448000]
sql execute -verbatim $db "INSERT INTO t1 (x, y) VALUES(9, ?);" \
[list param1 DateTime 1365984000]
- sql execute -execute reader -datetimeformat "MM/dd/yyyy HH:mm:ss" $db \
+ sql execute -execute reader -datetimeformat [getDateTimeFormat] $db \
"SELECT x, y FROM t1 ORDER BY x;"
foreach name [lsort -integer [array names rows -regexp {^\d+$}]] {
lappend result [list $name $rows($name)]
}
@@ -690,24 +690,24 @@
cleanupDb $fileName
unset -nocomplain name rows result db fileName
} -constraints \
{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} \
--result {{1 {{x 1} {y {04/15/2011 00:00:00}}}} {2 {{x 2} {y {04/15/2012\
-00:00:00}}}} {3 {{x 3} {y {04/15/2013 00:00:00}}}} {4 {{x 4} {y {04/15/2011\
-00:00:00}}}} {5 {{x 5} {y {04/15/2012 00:00:00}}}} {6 {{x 6} {y {04/15/2013\
-00:00:00}}}} {7 {{x 7} {y {04/15/2011 00:00:00}}}} {8 {{x 8} {y {04/15/2012\
-00:00:00}}}} {9 {{x 9} {y {04/15/2013 00:00:00}}}}}}
+-result {{1 {{x 1} {y {2011-04-15 00:00:00Z}}}} {2 {{x 2} {y {2012-04-15\
+00:00:00Z}}}} {3 {{x 3} {y {2013-04-15 00:00:00Z}}}} {4 {{x 4} {y {2011-04-15\
+00:00:00Z}}}} {5 {{x 5} {y {2012-04-15 00:00:00Z}}}} {6 {{x 6} {y {2013-04-15\
+00:00:00Z}}}} {7 {{x 7} {y {2011-04-15 00:00:00Z}}}} {8 {{x 8} {y {2012-04-15\
+00:00:00Z}}}} {9 {{x 9} {y {2013-04-15 00:00:00Z}}}}}}
###############################################################################
set date [clock format [clock seconds] -format yyyy-MM-dd]
###############################################################################
runTest {test basic-1.13 {DateTime using invariant culture} -setup {
- setupDb [set fileName basic-1.13.db] "" "" "DateTimeFormat=InvariantCulture;"
+ setupDb [set fileName basic-1.13.db] "" InvariantCulture Utc
} -body {
set result [list]
sql execute $db "CREATE TABLE t1(x INTEGER PRIMARY KEY ASC, y DATETIME);"
@@ -739,11 +739,11 @@
[list param1 DateTime 12:00]
sql execute $db "INSERT INTO t1 (x, y) VALUES(12, ?);" \
[list param1 DateTime "12/16/2009 12:00"]
- sql execute -execute reader -datetimeformat "MM/dd/yyyy HH:mm:ss" $db \
+ sql execute -execute reader -datetimeformat [getDateTimeFormat] $db \
"SELECT x, CAST(y AS TEXT) AS y2 FROM t1 ORDER BY x;"
foreach name [lsort -integer [array names rows -regexp {^\d+$}]] {
lappend result [list $name $rows($name)]
}
@@ -764,11 +764,11 @@
${date}T12:00:00.0000000Z}}} {12 {{x 12} {y2 2009-12-16T12:00:00.0000000Z}}}}]}
###############################################################################
runTest {test basic-1.14 {DateTime using current culture} -setup {
- setupDb [set fileName basic-1.14.db] "" "" "DateTimeFormat=CurrentCulture;"
+ setupDb [set fileName basic-1.14.db] "" CurrentCulture Utc
} -body {
set result [list]
sql execute $db "CREATE TABLE t1(x INTEGER PRIMARY KEY ASC, y DATETIME);"
@@ -800,11 +800,11 @@
[list param1 DateTime 12:00]
sql execute $db "INSERT INTO t1 (x, y) VALUES(12, ?);" \
[list param1 DateTime "12/16/2009 12:00"]
- sql execute -execute reader -datetimeformat "MM/dd/yyyy HH:mm:ss" $db \
+ sql execute -execute reader -datetimeformat [getDateTimeFormat] $db \
"SELECT x, CAST(y AS TEXT) AS y2 FROM t1 ORDER BY x;"
foreach name [lsort -integer [array names rows -regexp {^\d+$}]] {
lappend result [list $name $rows($name)]
}
Index: Tests/common.eagle
==================================================================
--- Tests/common.eagle
+++ Tests/common.eagle
@@ -106,17 +106,25 @@
#
# EagleShell.exe -initialize -postInitialize
# "object invoke Interpreter.GetActive AddRuntimeOption native"
# -file .\path\to\all.eagle
#
- if {[hasRuntimeOption native]} then {
- return [file join [file dirname $::path] bin [getBuildYear] \
- [machineToPlatform $::tcl_platform(machine)] \
- [getBuildConfiguration]]
+ if {[info exists ::build_directory] && \
+ [string length $::build_directory] > 0} then {
+ #
+ # NOTE: The location of the build directory has been overridden.
+ #
+ return $::build_directory
} else {
- return [file join [file dirname $::path] bin [getBuildYear] \
- [getBuildConfiguration] bin]
+ if {[hasRuntimeOption native]} then {
+ return [file join [file dirname $::path] bin [getBuildYear] \
+ [machineToPlatform $::tcl_platform(machine)] \
+ [getBuildConfiguration]]
+ } else {
+ return [file join [file dirname $::path] bin [getBuildYear] \
+ [getBuildConfiguration] bin]
+ }
}
}
proc getBuildFileName { fileName } {
return [file nativename \
@@ -129,11 +137,19 @@
# itself (i.e. the Eagle shell) is located. This will be used as
# the destination for the copied System.Data.SQLite native and
# managed assemblies (i.e. because this is one of the few places
# where the CLR will actually find and load them properly).
#
- return [info binary]
+ if {[info exists ::binary_directory] && \
+ [string length $::binary_directory] > 0} then {
+ #
+ # NOTE: The location of the binary directory has been overridden.
+ #
+ return $::binary_directory
+ } else {
+ return [info binary]
+ }
}
proc getBinaryFileName { fileName } {
return [file nativename \
[file join [getBinaryDirectory] [file tail $fileName]]]
@@ -247,10 +263,32 @@
tputs $channel [appendArgs "yes (" $version ")\n"]
} else {
tputs $channel no\n
}
}
+
+ proc getDateTimeFormat {} {
+ #
+ # NOTE: This procedure simply returns the "default" DateTime format used
+ # by the test suite.
+ #
+ if {[info exists ::datetime_format] && \
+ [string length $::datetime_format] > 0} then {
+ #
+ # NOTE: Return the manually overridden value for the DateTime format.
+ #
+ return $::datetime_format
+ } else {
+ #
+ # NOTE: Return an ISO8601 DateTime format compatible with SQLite,
+ # System.Data.SQLite, and suitable for round-tripping with the
+ # DateTime class of the framework. If this value is changed,
+ # various tests may fail.
+ #
+ return "yyyy-MM-dd HH:mm:ss.FFFFFFFK"
+ }
+ }
proc enumerableToList { enumerable } {
set result [list]
if {[string length $enumerable] == 0 || $enumerable eq "null"} then {
@@ -301,11 +339,13 @@
# result.
#
eval $command
}
- proc setupDb {fileName {mode ""} {delete ""} {extra ""} {varName db}} {
+ proc setupDb {
+ fileName {mode ""} {dateTimeFormat ""} {dateTimeKind ""} {extra ""}
+ {delete true} {varName db}} {
#
# NOTE: For now, all test databases used by the test suite are placed into
# the temporary directory. Each database used by a test should be
# cleaned up by that test using the "cleanupDb" procedure, below.
#
@@ -313,11 +353,11 @@
#
# NOTE: By default, delete any pre-existing database with the same file
# name.
#
- if {[string length $delete] == 0 || $delete} then {
+ if {$delete} then {
catch {file delete $fileName}
}
#
# NOTE: Refer to the specified variable (e.g. "db") in the context of the
@@ -336,10 +376,26 @@
# of the connection string now.
#
if {[string length $mode] > 0} then {
append connection {;Journal Mode=${mode}}
}
+
+ #
+ # NOTE: If the caller specified a DateTime format, add the necessary
+ # portion of the connection string now.
+ #
+ if {[string length $dateTimeFormat] > 0} then {
+ append connection {;DateTimeFormat=${dateTimeFormat}}
+ }
+
+ #
+ # NOTE: If the caller specified a DateTimeKind, add the necessary portion
+ # of the connection string now.
+ #
+ if {[string length $dateTimeKind] > 0} then {
+ append connection {;DateTimeKind=${dateTimeKind}}
+ }
#
# NOTE: If the caller specified an extra payload to the connection string,
# append it now.
#
ADDED Tests/tkt-343d392b51.eagle
Index: Tests/tkt-343d392b51.eagle
==================================================================
--- /dev/null
+++ Tests/tkt-343d392b51.eagle
@@ -0,0 +1,439 @@
+###############################################################################
+#
+# tkt-343d392b51.eagle --
+#
+# Written by Joe Mistachkin.
+# Released to the public domain, use at your own risk!
+#
+###############################################################################
+
+package require Eagle
+package require EagleLibrary
+package require EagleTest
+
+runTestPrologue
+
+###############################################################################
+
+package require System.Data.SQLite.Test
+runSQLiteTestPrologue
+
+###############################################################################
+
+set dateTimeFormats [list "" Ticks ISO8601 JulianDay UnixEpoch]
+
+for {set i 1} {$i < 5} {incr i} {
+ set dateTimeFormat [lindex $dateTimeFormats $i]
+
+ runTest {test [appendArgs tkt-343d392b51-1. $i] [subst {DateTime\
+ binding $dateTimeFormat format}] -setup {
+ setupDb [set fileName [appendArgs tkt-343d392b51-1. $i .db]] "" \
+ $dateTimeFormat Utc
+
+ set dateTime "4 October, 2011 3:27:50 PM GMT"
+ } -body {
+ sql execute $db "CREATE TABLE t1(x DATETIME);"
+
+ set paramDateTime1 [clock format [clock scan $dateTime] -format \
+ [getDateTimeFormat] -gmt true]
+
+ switch -exact -- $dateTimeFormat {
+ Ticks {
+ set paramDateTime1 [object invoke -alias DateTime Parse $paramDateTime1]
+ set paramDateTime1 [$paramDateTime1 ToUniversalTime.Ticks]
+ set paramDateTime2 $paramDateTime1
+ }
+ ISO8601 {
+ set paramDateTime2 [appendArgs ' $paramDateTime1 ']
+ }
+ JulianDay {
+ set paramDateTime1 [object invoke -alias DateTime Parse $paramDateTime1]
+ set paramDateTime1 [$paramDateTime1 -alias ToUniversalTime]
+
+ set paramDateTime1 [expr {[$paramDateTime1 ToOADate] + \
+ [object invoke -flags +NonPublic System.Data.SQLite.SQLiteConvert \
+ OleAutomationEpochAsJulianDay]}]
+
+ set paramDateTime2 $paramDateTime1
+ }
+ UnixEpoch {
+ set paramDateTime1 [clock scan $dateTime]
+ set paramDateTime2 $paramDateTime1
+ }
+ }
+
+ sql execute $db [appendArgs "INSERT INTO t1 (x) VALUES(" $paramDateTime2 \
+ ");"]
+
+ list [sql execute -verbatim -execute reader -format list -datetimeformat \
+ [getDateTimeFormat] $db "SELECT x FROM t1 WHERE x = ?;" \
+ [list param1 String $paramDateTime1]] \
+ [sql execute -verbatim -execute reader -format list -datetimeformat \
+ [getDateTimeFormat] $db "SELECT x FROM t1 WHERE x = ?;" \
+ [list param1 DateTime $paramDateTime1]]
+ } -cleanup {
+ cleanupDb $fileName
+
+ unset -nocomplain paramDateTime2 paramDateTime1 dateTime db fileName
+ } -constraints {eagle culture.en_US monoBug28 command.sql compile.DATA SQLite\
+System.Data.SQLite} -result {{{2011-10-04 15:27:50Z}} {{2011-10-04 15:27:50Z}}}}
+}
+
+###############################################################################
+
+unset -nocomplain dateTimeFormat i dateTimeFormats
+
+###############################################################################
+
+runTest {test tkt-343d392b51-2.1 {SQLiteDataAdapter update fail} -setup {
+ setupDb [set fileName tkt-343d392b51-2.1.db]
+ set otherFileName tkt-343d392b51-2.1-otherDb.db
+} -body {
+ set id [object invoke Interpreter.GetActive NextId]
+ set dataSource [file join [getTemporaryPath] $fileName]
+ set otherDataSource [file join [getTemporaryPath] $otherFileName]
+ set otherDbName otherDb
+ set otherTable [appendArgs $otherDbName .t1]
+
+ set sql(inserts) ""
+ set sql(1) [subst { \
+ ATTACH DATABASE '${otherDataSource}' AS ${otherDbName}; \
+ CREATE TABLE ${otherTable}(x INTEGER PRIMARY KEY, y DATETIME); \
+ [for {set i 1} {$i < 3} {incr i} {
+ append sql(inserts) [appendArgs \
+ "INSERT INTO " ${otherTable} " (x, y) VALUES(" $i ", '" \
+ [clock format $i -format [getDateTimeFormat]] "'); "]
+ }; return [expr {[info exists sql(inserts)] ? $sql(inserts) : ""}]] \
+ }]
+
+ set sql(2) [subst { \
+ SELECT x, y FROM ${otherTable} ORDER BY x; \
+ }]
+
+ unset -nocomplain results errors
+
+ set code [compileCSharpWith [subst {
+ using System;
+ using System.Data;
+ using System.Data.SQLite;
+
+ namespace _Dynamic${id}
+ {
+ public class Test${id}
+ {
+ public static void Main()
+ {
+ using (SQLiteConnection connection = new SQLiteConnection(
+ "Data Source=${dataSource};"))
+ {
+ connection.Open();
+
+ using (SQLiteCommand command = connection.CreateCommand())
+ {
+ command.CommandText = "${sql(1)}";
+ command.ExecuteNonQuery();
+ }
+
+ using (SQLiteDataAdapter dataAdapter = new SQLiteDataAdapter(
+ "${sql(2)}", connection))
+ {
+ using (DataSet dataSet = new DataSet())
+ {
+ dataAdapter.Fill(dataSet, "${otherTable}");
+
+ DataTable dataTable = dataSet.Tables\["${otherTable}"\];
+
+ dataTable.Columns\["x"\].Unique = true;
+ dataTable.PrimaryKey = new DataColumn\[\] {
+ dataTable.Columns\["x"\]
+ };
+
+ [expr {[isMono] ? "#pragma warning disable 219" : ""}]
+ SQLiteCommandBuilder commandBuilder =
+ new SQLiteCommandBuilder(dataAdapter);
+ [expr {[isMono] ? "#pragma warning restore 219" : ""}]
+
+ foreach (DataRow dataRow in dataTable.Rows)
+ {
+ //
+ // NOTE: Update even rows and delete odd rows.
+ //
+ if ((long)dataRow\["x"\] % 2 == 0)
+ dataRow\["y"\] =
+ DateTime.UtcNow.ToString("[getDateTimeFormat]");
+ else
+ dataRow.Delete();
+ }
+
+ dataAdapter.Update(dataTable); // DBConcurrencyException (?)
+ }
+ }
+ }
+ }
+ }
+ }
+ }] results errors System.Data.SQLite.dll]
+
+ list $code $results \
+ [expr {[info exists errors] ? $errors : ""}] \
+ [expr {$code eq "Ok" ? [catch {
+ object invoke _Dynamic${id}.Test${id} Main
+ } result] : [set result ""]}] $result
+} -cleanup {
+ cleanupDb $otherFileName
+ cleanupDb $fileName
+
+ unset -nocomplain result code results errors i sql otherTable otherDbName \
+ otherDataSource dataSource id db otherFileName fileName
+} -constraints \
+{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -match \
+glob -result {* System.Data.DBConcurrencyException: *}}
+
+###############################################################################
+
+runTest {test tkt-343d392b51-2.2 {SQLiteDataAdapter update success} -setup {
+ setupDb [set fileName tkt-343d392b51-2.2.db] "" JulianDay
+ set otherFileName tkt-343d392b51-2.2-otherDb.db
+} -body {
+ set id [object invoke Interpreter.GetActive NextId]
+ set dataSource [file join [getTemporaryPath] $fileName]
+ set otherDataSource [file join [getTemporaryPath] $otherFileName]
+ set otherDbName otherDb
+ set otherTable [appendArgs $otherDbName .t1]
+
+ set sql(inserts) ""
+ set sql(1) [subst { \
+ ATTACH DATABASE '${otherDataSource}' AS ${otherDbName}; \
+ CREATE TABLE ${otherTable}(x INTEGER PRIMARY KEY, y DATETIME); \
+ [for {set i 1} {$i < 3} {incr i} {
+ append sql(inserts) [appendArgs \
+ "INSERT INTO " ${otherTable} " (x, y) VALUES(" $i ", JULIANDAY('" \
+ [clock format $i -format [getDateTimeFormat]] "')); "]
+ }; return [expr {[info exists sql(inserts)] ? $sql(inserts) : ""}]] \
+ }]
+
+ set sql(2) [subst { \
+ SELECT x, y FROM ${otherTable} ORDER BY x; \
+ }]
+
+ unset -nocomplain results errors
+
+ set code [compileCSharpWith [subst {
+ using System;
+ using System.Data;
+ using System.Data.SQLite;
+
+ namespace _Dynamic${id}
+ {
+ public class Test${id}
+ {
+ public static void Main()
+ {
+ using (SQLiteConnection connection = new SQLiteConnection(
+ "Data Source=${dataSource};DateTimeFormat=JulianDay;"))
+ {
+ connection.Open();
+
+ using (SQLiteCommand command = connection.CreateCommand())
+ {
+ command.CommandText = "${sql(1)}";
+ command.ExecuteNonQuery();
+ }
+
+ using (SQLiteDataAdapter dataAdapter = new SQLiteDataAdapter(
+ "${sql(2)}", connection))
+ {
+ using (DataSet dataSet = new DataSet())
+ {
+ dataAdapter.Fill(dataSet, "${otherTable}");
+
+ DataTable dataTable = dataSet.Tables\["${otherTable}"\];
+
+ dataTable.Columns\["x"\].Unique = true;
+ dataTable.PrimaryKey = new DataColumn\[\] {
+ dataTable.Columns\["x"\]
+ };
+
+ [expr {[isMono] ? "#pragma warning disable 219" : ""}]
+ SQLiteCommandBuilder commandBuilder =
+ new SQLiteCommandBuilder(dataAdapter);
+ [expr {[isMono] ? "#pragma warning restore 219" : ""}]
+
+ foreach (DataRow dataRow in dataTable.Rows)
+ {
+ //
+ // NOTE: Update even rows and delete odd rows.
+ //
+ if ((long)dataRow\["x"\] % 2 == 0)
+ dataRow\["y"\] =
+ DateTime.UtcNow.ToString("[getDateTimeFormat]");
+ else
+ dataRow.Delete();
+ }
+
+ dataAdapter.Update(dataTable); // DBConcurrencyException (?)
+ }
+ }
+ }
+ }
+ }
+ }
+ }] results errors System.Data.SQLite.dll]
+
+ list $code $results \
+ [expr {[info exists errors] ? $errors : ""}] \
+ [expr {$code eq "Ok" ? [catch {
+ object invoke _Dynamic${id}.Test${id} Main
+ } result] : [set result ""]}] $result
+} -cleanup {
+ cleanupDb $otherFileName
+ cleanupDb $fileName
+
+ unset -nocomplain result code results errors i sql otherTable otherDbName \
+ otherDataSource dataSource id db otherFileName fileName
+} -constraints \
+{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -match \
+regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \{\}$}}
+
+###############################################################################
+
+runTest {test tkt-343d392b51-3.1 {attached database, same table name} -setup {
+ setupDb [set fileName tkt-343d392b51-3.1.db]
+ set otherFileName tkt-343d392b51-3.1-otherDb.db
+} -body {
+ set otherDataSource [file join [getTemporaryPath] $otherFileName]
+ set otherDbName otherDb
+ set otherTable [appendArgs $otherDbName .t1]
+
+ set sql(inserts) ""
+ set sql(1) [subst { \
+ CREATE TABLE t1(x INTEGER PRIMARY KEY); \
+ ATTACH DATABASE '${otherDataSource}' AS ${otherDbName}; \
+ CREATE TABLE ${otherTable}(x INTEGER PRIMARY KEY); \
+ [for {set i 1} {$i < 3} {incr i} {
+ append sql(inserts) [appendArgs \
+ "INSERT INTO t1 (x) VALUES(" $i "); "]
+
+ append sql(inserts) [appendArgs \
+ "INSERT INTO " ${otherTable} " (x) VALUES(" [expr {$i * 2}] "); "]
+ }; return [expr {[info exists sql(inserts)] ? $sql(inserts) : ""}]] \
+ }]
+
+ sql execute $db $sql(1)
+
+ list [sql execute -execute reader -format list $db "SELECT x FROM t1;"] \
+ [sql execute -execute reader -format list $db [appendArgs \
+ "SELECT x FROM " ${otherTable} ";"]]
+} -cleanup {
+ cleanupDb $otherFileName
+ cleanupDb $fileName
+
+ unset -nocomplain i sql otherTable otherDbName otherDataSource db \
+ otherFileName fileName
+} -constraints \
+{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result \
+{{1 2} {2 4}}}
+
+###############################################################################
+
+runTest {test tkt-343d392b51-3.2 {adapter, attached db, table names} -setup {
+ setupDb [set fileName tkt-343d392b51-3.2.db]
+ set otherFileName tkt-343d392b51-3.2-otherDb.db
+} -body {
+ set id [object invoke Interpreter.GetActive NextId]
+ set dataSource [file join [getTemporaryPath] $fileName]
+ set otherDataSource [file join [getTemporaryPath] $otherFileName]
+ set otherDbName otherDb
+ set otherTable [appendArgs $otherDbName .t1]
+
+ set sql(inserts) ""
+ set sql(1) [subst { \
+ CREATE TABLE t1(x INTEGER PRIMARY KEY); \
+ ATTACH DATABASE '${otherDataSource}' AS ${otherDbName}; \
+ CREATE TABLE ${otherTable}(x INTEGER PRIMARY KEY); \
+ [for {set i 1} {$i < 3} {incr i} {
+ append sql(inserts) [appendArgs \
+ "INSERT INTO t1 (x) VALUES(" $i ");"]
+ append sql(inserts) [appendArgs \
+ "INSERT INTO " ${otherTable} " (x) VALUES(" [expr {$i * 2}] "); "]
+ }; return [expr {[info exists sql(inserts)] ? $sql(inserts) : ""}]] \
+ }]
+
+ set sql(2) [subst { \
+ SELECT x FROM ${otherTable} ORDER BY x; \
+ }]
+
+ unset -nocomplain results errors
+
+ set code [compileCSharpWith [subst {
+ using System;
+ using System.Data;
+ using System.Data.SQLite;
+
+ namespace _Dynamic${id}
+ {
+ public class Test${id}
+ {
+ public static void Main()
+ {
+ using (SQLiteConnection connection = new SQLiteConnection(
+ "Data Source=${dataSource};"))
+ {
+ connection.Open();
+
+ using (SQLiteCommand command = connection.CreateCommand())
+ {
+ command.CommandText = "${sql(1)}";
+ command.ExecuteNonQuery();
+ }
+
+ using (SQLiteDataAdapter dataAdapter = new SQLiteDataAdapter(
+ "${sql(2)}", connection))
+ {
+ using (DataSet dataSet = new DataSet())
+ {
+ dataAdapter.Fill(dataSet, "${otherTable}");
+
+ DataTable dataTable = dataSet.Tables\["${otherTable}"\];
+
+ dataTable.Columns\["x"\].Unique = true;
+ dataTable.PrimaryKey = new DataColumn\[\] {
+ dataTable.Columns\["x"\]
+ };
+
+ [expr {[isMono] ? "#pragma warning disable 219" : ""}]
+ SQLiteCommandBuilder commandBuilder =
+ new SQLiteCommandBuilder(dataAdapter);
+ [expr {[isMono] ? "#pragma warning restore 219" : ""}]
+
+ foreach (DataRow dataRow in dataTable.Rows)
+ dataRow.Delete();
+
+ dataAdapter.Update(dataTable); // DBConcurrencyException (?)
+ }
+ }
+ }
+ }
+ }
+ }
+ }] results errors System.Data.SQLite.dll]
+
+ list $code $results \
+ [expr {[info exists errors] ? $errors : ""}] \
+ [expr {$code eq "Ok" ? [catch {
+ object invoke _Dynamic${id}.Test${id} Main
+ } result] : [set result ""]}] $result
+} -cleanup {
+ cleanupDb $otherFileName
+ cleanupDb $fileName
+
+ unset -nocomplain result code results errors i sql otherTable otherDbName \
+ otherDataSource dataSource id db otherFileName fileName
+} -constraints \
+{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -match \
+regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \{\}$}}
+
+###############################################################################
+
+runSQLiteTestEpilogue
+runTestEpilogue
Index: Tests/tkt-448d663d11.eagle
==================================================================
--- Tests/tkt-448d663d11.eagle
+++ Tests/tkt-448d663d11.eagle
@@ -34,11 +34,11 @@
runTest {test tkt-448d663d11-1.2 {missing journal mode, WAL db} -body {
set fileName tkt-448d663d11-1.2.db
file copy -force [file join $path wal.db] \
[file join [getTemporaryPath] $fileName]
- setupDb $fileName "" false
+ setupDb $fileName "" "" "" "" false
sql execute -execute scalar $db "PRAGMA journal_mode;"
} -cleanup {
cleanupDb $fileName
unset -nocomplain db fileName
} -constraints \
@@ -49,11 +49,11 @@
runTest {test tkt-448d663d11-1.3 {missing journal mode, non-WAL db} -body {
set fileName tkt-448d663d11-1.3.db
file copy -force [file join $path nonWal.db] \
[file join [getTemporaryPath] $fileName]
- setupDb $fileName "" false
+ setupDb $fileName "" "" "" "" false
sql execute -execute scalar $db "PRAGMA journal_mode;"
} -cleanup {
cleanupDb $fileName
unset -nocomplain db fileName
} -constraints \
@@ -76,11 +76,11 @@
runTest {test tkt-448d663d11-1.5 {'Default' journal mode, WAL db} -body {
set fileName tkt-448d663d11-1.5.db
file copy -force [file join $path wal.db] \
[file join [getTemporaryPath] $fileName]
- setupDb $fileName Default false
+ setupDb $fileName Default "" "" "" false
sql execute -execute scalar $db "PRAGMA journal_mode;"
} -cleanup {
cleanupDb $fileName
unset -nocomplain db fileName
} -constraints \
@@ -91,11 +91,11 @@
runTest {test tkt-448d663d11-1.6 {'Default' journal mode, non-WAL db} -body {
set fileName tkt-448d663d11-1.6.db
file copy -force [file join $path nonWal.db] \
[file join [getTemporaryPath] $fileName]
- setupDb $fileName Default false
+ setupDb $fileName Default "" "" "" false
sql execute -execute scalar $db "PRAGMA journal_mode;"
} -cleanup {
cleanupDb $fileName
unset -nocomplain db fileName
} -constraints \
@@ -118,11 +118,11 @@
runTest {test tkt-448d663d11-1.8 {'Delete' journal mode, WAL db} -body {
set fileName tkt-448d663d11-1.8.db
file copy -force [file join $path wal.db] \
[file join [getTemporaryPath] $fileName]
- setupDb $fileName Delete false
+ setupDb $fileName Delete "" "" "" false
sql execute -execute scalar $db "PRAGMA journal_mode;"
} -cleanup {
cleanupDb $fileName
unset -nocomplain db fileName
} -constraints \
@@ -133,11 +133,11 @@
runTest {test tkt-448d663d11-1.9 {'Delete' journal mode, non-WAL db} -body {
set fileName tkt-448d663d11-1.9.db
file copy -force [file join $path nonWal.db] \
[file join [getTemporaryPath] $fileName]
- setupDb $fileName Delete false
+ setupDb $fileName Delete "" "" "" false
sql execute -execute scalar $db "PRAGMA journal_mode;"
} -cleanup {
cleanupDb $fileName
unset -nocomplain db fileName
} -constraints \
@@ -208,11 +208,11 @@
runTest {test tkt-448d663d11-1.15 {'Wal' journal mode, non-WAL db} -body {
set fileName tkt-448d663d11-1.15.db
file copy -force [file join $path nonWal.db] \
[file join [getTemporaryPath] $fileName]
- setupDb $fileName Wal false
+ setupDb $fileName Wal "" "" "" false
sql execute -execute scalar $db "PRAGMA journal_mode;"
} -cleanup {
cleanupDb $fileName
unset -nocomplain db fileName
} -constraints \
@@ -223,11 +223,11 @@
runTest {test tkt-448d663d11-1.16 {'Wal' journal mode, WAL db} -body {
set fileName tkt-448d663d11-1.16.db
file copy -force [file join $path wal.db] \
[file join [getTemporaryPath] $fileName]
- setupDb $fileName Wal false
+ setupDb $fileName Wal "" "" "" false
sql execute -execute scalar $db "PRAGMA journal_mode;"
} -cleanup {
cleanupDb $fileName
unset -nocomplain db fileName
} -constraints \
@@ -250,11 +250,11 @@
runTest {test tkt-448d663d11-1.18 {'Bad' journal mode, non-WAL db} -body {
set fileName tkt-448d663d11-1.18.db
file copy -force [file join $path nonWal.db] \
[file join [getTemporaryPath] $fileName]
- setupDb $fileName Bad false
+ setupDb $fileName Bad "" "" "" false
sql execute -execute scalar $db "PRAGMA journal_mode;"
} -cleanup {
cleanupDb $fileName
unset -nocomplain db fileName
} -constraints \
@@ -265,11 +265,11 @@
runTest {test tkt-448d663d11-1.19 {'Bad' journal mode, WAL db} -body {
set fileName tkt-448d663d11-1.19.db
file copy -force [file join $path wal.db] \
[file join [getTemporaryPath] $fileName]
- setupDb $fileName Bad false
+ setupDb $fileName Bad "" "" "" false
sql execute -execute scalar $db "PRAGMA journal_mode;"
} -cleanup {
cleanupDb $fileName
unset -nocomplain db fileName
} -constraints \
Index: Tests/tkt-b4a7ddc83f.eagle
==================================================================
--- Tests/tkt-b4a7ddc83f.eagle
+++ Tests/tkt-b4a7ddc83f.eagle
@@ -30,14 +30,14 @@
}
###############################################################################
for {set i 1} {$i < 3} {incr i} {
- runTest {test tkt-b4a7ddc83f-1.$i {logging shutdown} -setup \
+ runTest {test [appendArgs tkt-b4a7ddc83f-1. $i] {logging shutdown} -setup \
[getAppDomainPreamble {
set appDomainId(1) {[object invoke AppDomain.CurrentDomain Id]}
- set fileName {tkt-b4a7ddc83f-1.$i.db}
+ set fileName {[appendArgs tkt-b4a7ddc83f-1. $i .db]}
}] -body {
set appDomainId(2) [object invoke AppDomain.CurrentDomain Id]
package require EagleLibrary
package require EagleTest