Index: Externals/Eagle/lib/Eagle1.0/vendor.eagle
==================================================================
--- Externals/Eagle/lib/Eagle1.0/vendor.eagle
+++ Externals/Eagle/lib/Eagle1.0/vendor.eagle
@@ -21,10 +21,13 @@
# initialization and/or customizations in here. Additionally, this file
# may contain per-interpreter customizations required when porting to
# new platforms, operating systems, etc.
#
+###############################################################################
+############################## BEGIN VENDOR CODE ##############################
+###############################################################################
#
# NOTE: Use our own namespace here because even though we do not directly
# support namespaces ourselves, we do not want to pollute the global
# namespace if this script actually ends up being evaluated in Tcl.
#
@@ -76,11 +79,21 @@
puts -nonewline $channel [appendArgs \
"Found vendor-specific test package directory \"" $dir \
"\", adding...\n"]
}
+ #
+ # NOTE: Append the directory to the necessary environment variable
+ # so that it will get picked up when Eagle actually rebuilds
+ # the auto-path list (below).
+ #
lappend ::env(EAGLELIBPATH) $dir
+
+ #
+ # NOTE: Force Eagle to rebuild the auto-path list for the current
+ # interpreter right now.
+ #
object invoke Utility RefreshAutoPathList
}
#
# NOTE: We are done, return success.
@@ -105,9 +118,35 @@
#
# NOTE: Directory not found, return failure.
#
return false
}
+
+ proc checkForTestOverrides { channel varNames quiet } {
+ set result 0
+
+ foreach varName $varNames {
+ if {[uplevel 1 [list info exists $varName]]} then {
+ incr result
+
+ if {!$quiet} then {
+ puts -nonewline $channel [appendArgs \
+ "Found vendor-specific test override \"" $varName "\".\n"]
+ }
+ }
+ }
+
+ return $result
+ }
+
+ checkForTestOverrides stdout \
+ [list binary_directory build_base_directory build_directory \
+ common_directory datetime_format test_configuration \
+ test_year] false
addTestSuiteToAutoPath stdout false
}
}
+
+###############################################################################
+############################### END VENDOR CODE ###############################
+###############################################################################
Index: System.Data.SQLite/SQLiteDataReader.cs
==================================================================
--- System.Data.SQLite/SQLiteDataReader.cs
+++ System.Data.SQLite/SQLiteDataReader.cs
@@ -5,11 +5,12 @@
* Released to the public domain, use at your own risk!
********************************************************/
namespace System.Data.SQLite
{
- using System;
+ using System;
+ using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Globalization;
///
@@ -583,15 +584,143 @@
///
/// Returns a DataTable containing the schema information for the active SELECT statement being processed.
public override DataTable GetSchemaTable()
{
return GetSchemaTable(true, false);
+ }
+
+ private class ColumnParent : IEqualityComparer
+ {
+ public string DatabaseName;
+ public string TableName;
+ public string ColumnName;
+
+ public ColumnParent()
+ {
+ // do nothing.
+ }
+
+ public ColumnParent(
+ string databaseName,
+ string tableName,
+ string columnName
+ )
+ : this()
+ {
+ this.DatabaseName = databaseName;
+ this.TableName = tableName;
+ this.ColumnName = columnName;
+ }
+
+ #region IEqualityComparer Members
+ public bool Equals(ColumnParent x, ColumnParent y)
+ {
+ if ((x == null) && (y == null))
+ {
+ return true;
+ }
+ else if ((x == null) || (y == null))
+ {
+ return false;
+ }
+ else
+ {
+ if (!String.Equals(x.DatabaseName, y.DatabaseName,
+ StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ if (!String.Equals(x.TableName, y.TableName,
+ StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ if (!String.Equals(x.ColumnName, y.ColumnName,
+ StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ public int GetHashCode(ColumnParent obj)
+ {
+ int result = 0;
+
+ if ((obj != null) && (obj.DatabaseName != null))
+ result ^= obj.DatabaseName.GetHashCode();
+
+ if ((obj != null) && (obj.TableName != null))
+ result ^= obj.TableName.GetHashCode();
+
+ if ((obj != null) && (obj.ColumnName != null))
+ result ^= obj.ColumnName.GetHashCode();
+
+ return result;
+ }
+ #endregion
+ }
+
+ private static void GetStatementColumnParents(
+ SQLiteBase sql,
+ SQLiteStatement stmt,
+ int fieldCount,
+ ref Dictionary> parentToColumns,
+ ref Dictionary columnToParent
+ )
+ {
+ if (parentToColumns == null)
+ parentToColumns = new Dictionary>(
+ new ColumnParent());
+
+ if (columnToParent == null)
+ columnToParent = new Dictionary();
+
+ for (int n = 0; n < fieldCount; n++)
+ {
+ string databaseName = sql.ColumnDatabaseName(stmt, n);
+ string tableName = sql.ColumnTableName(stmt, n);
+ string columnName = sql.ColumnOriginalName(stmt, n);
+
+ ColumnParent key = new ColumnParent(databaseName, tableName, null);
+ ColumnParent value = new ColumnParent(databaseName, tableName, columnName);
+
+ if (!parentToColumns.ContainsKey(key))
+ parentToColumns.Add(key, new List(new int[] { n }));
+ else
+ parentToColumns[key].Add(n);
+
+ columnToParent.Add(n, value);
+ }
}
internal DataTable GetSchemaTable(bool wantUniqueInfo, bool wantDefaultValue)
{
- CheckClosed();
+ CheckClosed();
+
+ //
+ // BUGFIX: We need to quickly scan all the fields in the current
+ // "result set" to see how many distinct tables are actually
+ // involved. This information is necessary so that some
+ // intelligent decisions can be made when constructing the
+ // metadata below. For example, we need to be very careful
+ // about flagging a particular column as "unique" just
+ // because it was in its original underlying database table
+ // if there are now multiple tables involved in the
+ // "result set". See ticket [7e3fa93744] for more detailed
+ // information.
+ //
+ Dictionary> parentToColumns = null;
+ Dictionary columnToParent = null;
+
+ GetStatementColumnParents(
+ _command.Connection._sql, _activeStatement, _fieldCount,
+ ref parentToColumns, ref columnToParent);
DataTable tbl = new DataTable("SchemaTable");
DataTable tblIndexes = null;
DataTable tblIndexColumns;
DataRow row;
@@ -649,22 +778,22 @@
row[SchemaTableColumn.IsUnique] = false;
row[SchemaTableColumn.IsKey] = false;
row[SchemaTableOptionalColumn.IsAutoIncrement] = false;
row[SchemaTableColumn.DataType] = GetFieldType(n);
row[SchemaTableOptionalColumn.IsHidden] = false;
- row[SchemaTableColumn.BaseSchemaName] = _baseSchemaName;
-
- strColumn = _command.Connection._sql.ColumnOriginalName(_activeStatement, n);
+ row[SchemaTableColumn.BaseSchemaName] = _baseSchemaName;
+
+ strColumn = columnToParent[n].ColumnName;
if (String.IsNullOrEmpty(strColumn) == false) row[SchemaTableColumn.BaseColumnName] = strColumn;
row[SchemaTableColumn.IsExpression] = String.IsNullOrEmpty(strColumn);
- row[SchemaTableColumn.IsAliased] = (String.Compare(GetName(n), strColumn, StringComparison.OrdinalIgnoreCase) != 0);
-
- temp = _command.Connection._sql.ColumnTableName(_activeStatement, n);
- if (String.IsNullOrEmpty(temp) == false) row[SchemaTableColumn.BaseTableName] = temp;
-
- temp = _command.Connection._sql.ColumnDatabaseName(_activeStatement, n);
+ row[SchemaTableColumn.IsAliased] = (String.Compare(GetName(n), strColumn, StringComparison.OrdinalIgnoreCase) != 0);
+
+ temp = columnToParent[n].TableName;
+ if (String.IsNullOrEmpty(temp) == false) row[SchemaTableColumn.BaseTableName] = temp;
+
+ temp = columnToParent[n].DatabaseName;
if (String.IsNullOrEmpty(temp) == false) row[SchemaTableOptionalColumn.BaseCatalogName] = temp;
string dataType = null;
// If we have a table-bound column, extract the extra information from it
if (String.IsNullOrEmpty(strColumn) == false)
@@ -759,12 +888,18 @@
null
});
foreach (DataRow rowColumnIndex in tblIndexColumns.Rows)
{
if (String.Compare((string)rowColumnIndex["COLUMN_NAME"], strColumn, StringComparison.OrdinalIgnoreCase) == 0)
- {
- if (tblIndexColumns.Rows.Count == 1 && (bool)row[SchemaTableColumn.AllowDBNull] == false)
+ {
+ //
+ // BUGFIX: Make sure that we only flag this column as "unique"
+ // if we are not processing of some kind of multi-table
+ // construct (i.e. a join) because in that case we must
+ // allow duplicate values (refer to ticket [7e3fa93744]).
+ //
+ if (parentToColumns.Count == 1 && tblIndexColumns.Rows.Count == 1 && (bool)row[SchemaTableColumn.AllowDBNull] == false)
row[SchemaTableColumn.IsUnique] = rowIndexes["UNIQUE"];
// If its an integer primary key and the only primary key in the table, then its a rowid alias and is autoincrement
// NOTE: Currently commented out because this is not always the desired behavior. For example, a 1:1 relationship with
// another table, where the other table is autoincrement, but this one is not, and uses the rowid from the other.
ADDED Tests/tkt-7e3fa93744.eagle
Index: Tests/tkt-7e3fa93744.eagle
==================================================================
--- /dev/null
+++ Tests/tkt-7e3fa93744.eagle
@@ -0,0 +1,139 @@
+###############################################################################
+#
+# tkt-7e3fa93744.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
+
+###############################################################################
+
+runTest {test tkt-7e3fa93744-1.1 {composite primary key, baseline} -setup {
+ setupDb [set fileName tkt-7e3fa93744-1.1.db]
+} -body {
+ set sql {
+ CREATE TABLE t1 (
+ id1 INTEGER PRIMARY KEY
+ );
+
+ CREATE TABLE t2 (
+ id1 INTEGER NOT NULL,
+ id2 INTEGER NOT NULL,
+ PRIMARY KEY (id1, id2)
+ );
+
+ INSERT INTO t1 (id1) VALUES (1);
+ INSERT INTO t1 (id1) VALUES (2);
+
+ INSERT INTO t2 (id1, id2) VALUES (1, 1);
+ INSERT INTO t2 (id1, id2) VALUES (1, 2);
+ INSERT INTO t2 (id1, id2) VALUES (2, 1);
+ INSERT INTO t2 (id1, id2) VALUES (2, 2);
+
+ SELECT t1.id1, t2.id1, t2.id2
+ FROM t1, t2
+ ORDER BY t1.id1, t2.id1, t2.id2;
+ }
+
+ sql execute -execute reader -format list $db $sql
+} -cleanup {
+ cleanupDb $fileName
+
+ unset -nocomplain sql db fileName
+} -constraints \
+{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result \
+{1 1 1 1 1 2 1 2 1 1 2 2 2 1 1 2 1 2 2 2 1 2 2 2}}
+
+###############################################################################
+
+runTest {test tkt-7e3fa93744-1.2 {composite primary key, DataTable} -setup {
+ setupDb [set fileName tkt-7e3fa93744-1.2.db]
+} -body {
+ set id [object invoke Interpreter.GetActive NextId]
+ set dataSource [file join [getTemporaryPath] $fileName]
+
+ set sql { \
+ CREATE TABLE t1 ( \
+ id1 INTEGER PRIMARY KEY NOT NULL \
+ ); \
+ CREATE TABLE t2 ( \
+ id1 INTEGER NOT NULL, \
+ id2 INTEGER NOT NULL, \
+ PRIMARY KEY (id1, id2) \
+ ); \
+ INSERT INTO t1 (id1) VALUES (1); \
+ INSERT INTO t1 (id1) VALUES (2); \
+ INSERT INTO t2 (id1, id2) VALUES (1, 1); \
+ INSERT INTO t2 (id1, id2) VALUES (1, 2); \
+ INSERT INTO t2 (id1, id2) VALUES (2, 1); \
+ INSERT INTO t2 (id1, id2) VALUES (2, 2); \
+ SELECT t1.id1, t2.id1, t2.id2 \
+ FROM t1, t2 \
+ ORDER BY t1.id1, t2.id1, t2.id2; \
+ }
+
+ 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 int Main()
+ {
+ using (SQLiteConnection connection = new SQLiteConnection(
+ "Data Source=${dataSource};"))
+ {
+ connection.Open();
+
+ using (SQLiteCommand command = connection.CreateCommand())
+ {
+ command.CommandText = "${sql}";
+
+ using (SQLiteDataReader dataReader = command.ExecuteReader())
+ {
+ DataTable dataTable = new DataTable();
+ dataTable.Load(dataReader);
+
+ return dataTable.Rows.Count;
+ }
+ }
+ }
+ }
+ }
+ }
+ }] 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 $fileName
+
+ unset -nocomplain result code results errors sql dataSource id db fileName
+} -constraints \
+{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -match \
+regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 8$}}
+
+###############################################################################
+
+runSQLiteTestEpilogue
+runTestEpilogue