diff --git a/DuckDB.NET.Data/DuckDBSchema.cs b/DuckDB.NET.Data/DuckDBSchema.cs index 869ea5e..89db34e 100644 --- a/DuckDB.NET.Data/DuckDBSchema.cs +++ b/DuckDB.NET.Data/DuckDBSchema.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Text; +using DuckDB.NET.Native; + namespace DuckDB.NET.Data; internal static class DuckDBSchema @@ -20,6 +22,7 @@ internal static class DuckDBSchema collectionName.ToUpperInvariant() switch { "METADATACOLLECTIONS" => GetMetaDataCollections(), + "DATASOURCEINFORMATION" => GetDataSourceInformation(connection.ServerVersion), "RESTRICTIONS" => GetRestrictions(), "RESERVEDWORDS" => GetReservedWords(connection), "TABLES" => GetTables(connection, restrictionValues), @@ -41,6 +44,7 @@ internal static class DuckDBSchema Rows = { { DbMetaDataCollectionNames.MetaDataCollections, 0, 0 }, + { DbMetaDataCollectionNames.DataSourceInformation, 0, 0 }, { DbMetaDataCollectionNames.Restrictions, 0, 0 }, { DbMetaDataCollectionNames.ReservedWords, 0, 0 }, { DuckDbMetaDataCollectionNames.Tables, TableRestrictions.Length, 3 }, @@ -50,6 +54,62 @@ internal static class DuckDBSchema } }; + private static DataTable GetDataSourceInformation(string? serverVersion) => + new(DbMetaDataCollectionNames.DataSourceInformation) { + Columns = + { + { DbMetaDataColumnNames.CompositeIdentifierSeparatorPattern, typeof(string) }, + { DbMetaDataColumnNames.DataSourceProductName, typeof(string) }, + { DbMetaDataColumnNames.DataSourceProductVersion, typeof(string) }, + { DbMetaDataColumnNames.DataSourceProductVersionNormalized, typeof(string) }, + { DbMetaDataColumnNames.GroupByBehavior, typeof(GroupByBehavior) }, + { DbMetaDataColumnNames.IdentifierPattern, typeof(string) }, + { DbMetaDataColumnNames.IdentifierCase, typeof(IdentifierCase) }, + { DbMetaDataColumnNames.OrderByColumnsInSelect, typeof(bool) }, + { DbMetaDataColumnNames.ParameterMarkerFormat, typeof(string) }, + { DbMetaDataColumnNames.ParameterMarkerPattern, typeof(string) }, + { DbMetaDataColumnNames.ParameterNameMaxLength, typeof(int) }, + { DbMetaDataColumnNames.ParameterNamePattern, typeof(string) }, + { DbMetaDataColumnNames.QuotedIdentifierPattern, typeof(string) }, + { DbMetaDataColumnNames.QuotedIdentifierCase, typeof(IdentifierCase) }, + { DbMetaDataColumnNames.StatementSeparatorPattern, typeof(string) }, + { DbMetaDataColumnNames.StringLiteralPattern, typeof(string) }, + { DbMetaDataColumnNames.SupportedJoinOperators, typeof(SupportedJoinOperators) } + }, + Rows = + { + // For more information about pattern, parameters and so on go to: + // https://duckdb.org/docs/sql/keywords_and_identifiers.html + // https://duckdb.org/docs/sql/query_syntax/prepared_statements + { + "\\.", + "DuckDB", + serverVersion, + serverVersion, + GroupByBehavior.Unrelated, + // strings that begin with a single special character ($, letter, or underscore) followed by zero or more alphanumeric characters, underscore, $ + // or strings that begin with a double quote followed by one or more any characters (except double quotes or null character) + "(^\\[\\p{Lo}\\p{Lu}\\p{Ll}_$][\\p{Lo}\\p{Lu}\\p{Ll}\\p{Nd}$$_]*$)|(^\\\"[^\\\"\\0]|\\\"\\\"+\\\"$)", + IdentifierCase.Insensitive, + false, + "{0}", + // identifies strings composed of letters, numbers, underscores and $ + "$[\\p{Lo}\\p{Lu}\\p{Ll}\\p{Lm}_$][\\p{Lo}\\p{Lu}\\p{Ll}\\p{Lm}\\p{Nd}\\uff3f_$\\$]*(?=\\s+|$)", + 128, + // This regexp matches a string that begins with a letter, number, underscore, or dollar sign, and can contain other similar characters, numbers, question marks, underscores, dollar signs, and white spaces. + // The string must end with white space or the end of the string. + "^[\\p{Lo}\\p{Lu}\\p{Ll}\\p{Lm}_$][\\p{Lo}\\p{Lu}\\p{Ll}\\p{Lm}\\p{Nd}\\uff3f_$\\$]*(?=\\s+|$)", + // This regexp is used to find a sequence of characters that does not contain double quote or contains them in pairs. + "((\"^\\\"\"|\\\"\\\")*)", + IdentifierCase.Insensitive, + ";", + // The regexp is used to find a sequence of one or more characters, excluding single quotes ('). + "'(([^']|'')*)'", + SupportedJoinOperators.Inner | SupportedJoinOperators.LeftOuter | SupportedJoinOperators.RightOuter | SupportedJoinOperators.FullOuter + } + } + }; + private static DataTable GetRestrictions() => new(DbMetaDataCollectionNames.Restrictions) { diff --git a/DuckDB.NET.Test/SchemaTests.cs b/DuckDB.NET.Test/SchemaTests.cs index fb77d0f..dce304c 100644 --- a/DuckDB.NET.Test/SchemaTests.cs +++ b/DuckDB.NET.Test/SchemaTests.cs @@ -176,6 +176,31 @@ public void IndexesTooManyRestrictions() Assert.Throws(() => Connection.GetSchema("Indexes", new string [5])); } + [Fact] + public void DataSourceInformation() { + var schema = Connection.GetSchema(DbMetaDataCollectionNames.DataSourceInformation); + Assert.NotEmpty(schema.Rows); + + Assert.Equal(1, schema.Rows.Count); + Assert.Equal("\\.", schema.Rows[0][DbMetaDataColumnNames.CompositeIdentifierSeparatorPattern]); + Assert.Equal("duckdb", schema.Rows[0][DbMetaDataColumnNames.DataSourceProductName]); + Assert.Equal(Connection.ServerVersion, schema.Rows[0][DbMetaDataColumnNames.DataSourceProductVersion]); + Assert.Equal(Connection.ServerVersion, schema.Rows[0][DbMetaDataColumnNames.DataSourceProductVersionNormalized]); + Assert.Equal(GroupByBehavior.Unrelated, (GroupByBehavior)schema.Rows[0][DbMetaDataColumnNames.GroupByBehavior]); + Assert.Equal("(^\\[\\p{Lo}\\p{Lu}\\p{Ll}_@#][\\p{Lo}\\p{Lu}\\p{Ll}\\p{Nd}@$#_]*$)|(^\\[[^\\]\\0]|\\]\\]+\\]$)|(^\\\"[^\\\"\\0]|\\\"\\\"+\\\"$)", schema.Rows[0][DbMetaDataColumnNames.IdentifierPattern]); + Assert.Equal(IdentifierCase.Insensitive, (IdentifierCase)schema.Rows[0][DbMetaDataColumnNames.IdentifierCase]); + Assert.Equal(false, schema.Rows[0][DbMetaDataColumnNames.OrderByColumnsInSelect]); + Assert.Equal("{0}", schema.Rows[0][DbMetaDataColumnNames.ParameterMarkerFormat]); + Assert.Equal("$[\\p{Lo}\\p{Lu}\\p{Ll}\\p{Lm}_@#][\\p{Lo}\\p{Lu}\\p{Ll}\\p{Lm}\\p{Nd}\\uff3f_@#\\$]*(?=\\s+|$)", schema.Rows[0][DbMetaDataColumnNames.ParameterMarkerPattern]); + Assert.Equal(128, schema.Rows[0][DbMetaDataColumnNames.ParameterNameMaxLength]); + Assert.Equal("^[\\p{Lo}\\p{Lu}\\p{Ll}\\p{Lm}_@#][\\p{Lo}\\p{Lu}\\p{Ll}\\p{Lm}\\p{Nd}\\uff3f_@#\\$]*(?=\\s+|$)", schema.Rows[0][DbMetaDataColumnNames.ParameterNamePattern]); + Assert.Equal("(([^\\[]|\\]\\])*)", schema.Rows[0][DbMetaDataColumnNames.QuotedIdentifierPattern]); + Assert.Equal(IdentifierCase.Insensitive, (IdentifierCase)schema.Rows[0][DbMetaDataColumnNames.QuotedIdentifierCase]); + Assert.Equal(";", schema.Rows[0][DbMetaDataColumnNames.StatementSeparatorPattern]); + Assert.Equal("'(([^']|'')*)'", schema.Rows[0][DbMetaDataColumnNames.StringLiteralPattern]); + Assert.Equal(SupportedJoinOperators.Inner | SupportedJoinOperators.LeftOuter | SupportedJoinOperators.RightOuter | SupportedJoinOperators.FullOuter, (SupportedJoinOperators)schema.Rows[0][DbMetaDataColumnNames.SupportedJoinOperators]); + } + private static IEnumerable GetValues(DataTable schema, string columnName) => schema.Rows.Cast().Select(r => (string)r[columnName]); } \ No newline at end of file