Add DuckDb查询支持

This commit is contained in:
lihanbo 2025-01-06 12:19:00 +08:00
parent bad4552f16
commit bca7469cfa
9 changed files with 514 additions and 25 deletions

View File

@ -21,7 +21,11 @@
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<UseWPF>true</UseWPF>
<ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
<OptimizationPreference>Size</OptimizationPreference>
</PropertyGroup>
<ItemGroup>
<None Remove="HL\SQL.xshd" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.8.1">
<Visible>False</Visible>
@ -35,9 +39,17 @@
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="HL\SQL.xshd">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<PackageReference Include="AvalonEdit" Version="6.3.0.90" />
<PackageReference Include="CommunityToolkit.Mvvm">
<Version>8.3.2</Version>
</PackageReference>
<PackageReference Include="DuckDB.NET.Bindings.Full" Version="1.1.3" />
<PackageReference Include="DuckDB.NET.Data.Full" Version="1.1.3" />
<PackageReference Include="HandyControl">
<Version>3.5.1</Version>
</PackageReference>

289
HL/SQL.xshd Normal file
View File

@ -0,0 +1,289 @@
<?xml version="1.0"?>
<!-- Shades of Red-Brown: #a31515, #cf4315, #ffb96e, #ffdc95 -->
<!-- Shades of Bright-Blue: #0077dc #008fe4 #8dbbdc #8de8ff -->
<SyntaxDefinition name="SQL" extensions=".sql"
xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
<!-- T-SQL Reference: http://msdn.microsoft.com/de-de/library/ms189826%28v=sql.90%29.aspx -->
<Color name="Digits" foreground="DarkBlue" exampleText="3.1415f"/>
<Color name="Comment" foreground="Green" exampleText="string text = &quot;Hello, World!&quot;"/>
<Color name="Punctuation" foreground="Red" exampleText="string text = &quot;Hello, World!&quot;"/>
<Color name="String" foreground="Olive" exampleText="string text = &quot;Hello, World!&quot;"/>
<Color name="String2" foreground="#993" exampleText="string text = &quot;Hello, World!&quot;"/>
<Color name="Keyword" fontWeight="bold" foreground="Blue" exampleText="SELECT"/>
<Color name="Keyword1" fontWeight="normal" foreground="Blue" exampleText="NOCOUNT"/>
<Color name="GoKeyword" fontWeight="bold" foreground="Red" exampleText="GO"/>
<Color name="MethodCall" foreground="MidnightBlue" fontWeight="bold" />
<Color name="Variable" foreground="Red" exampleText="@Variable" />
<Color name="Variable1" foreground="Red" exampleText="@@Variable" />
<Color name="ObjectReference" foreground="Teal" exampleText="Customer.Name" />
<Color name="ObjectReference1" foreground="Teal" exampleText="dbo.Customer.Name" />
<Color name="ObjectReferenceInBrackets" foreground="Teal" exampleText="[Customer].[Name]" />
<Color name="ObjectReferenceInBrackets1" foreground="Teal" exampleText="[dbo].[Customer].[Name]" />
<Color name="CommentMarkerSetTodo" foreground="Red" fontWeight="bold" />
<Color name="CommentMarkerSetHackUndone" foreground="#E0E000" fontWeight="bold" />
<RuleSet name="CommentMarkerSet">
<Keywords color="CommentMarkerSetTodo">
<Word>TODO</Word>
<Word>FIXME</Word>
</Keywords>
<Keywords color="CommentMarkerSetHackUndone">
<Word>HACK</Word>
<Word>UNDONE</Word>
</Keywords>
</RuleSet>
<RuleSet ignoreCase="true">
<Span color="String" multiline="true" >
<Begin>'</Begin>
<End>'</End>
</Span>
<Span color="String2" multiline="true" >
<Begin>"</Begin>
<End>"</End>
</Span>
<!-- span for escape sequences -->
<Span color="Comment" begin="--" end="\n" ruleSet="CommentMarkerSet"/>
<Span color="Comment" multiline="true" ruleSet="CommentMarkerSet">
<Begin>/\*</Begin>
<End>\*/</End>
</Span>
<Keywords color="Keyword" >
<Word>ABORT</Word>
<Word>BETWEEN</Word>
<Word>CRASH</Word>
<Word>DIGITS</Word>
<Word>ACCEPT</Word>
<Word>BINARY_INTEGER</Word>
<Word>CREATE</Word>
<Word>DISPOSE</Word>
<Word>ACCESS</Word>
<Word>BODY</Word>
<Word>CURRENT</Word>
<Word>DISTINCT</Word>
<Word>ADD</Word>
<Word>BOOLEAN</Word>
<Word>CURRVAL</Word>
<Word>DO</Word>
<Word>ALL</Word>
<Word>BY</Word>
<Word>CURSOR</Word>
<Word>DROP</Word>
<Word>ALTER</Word>
<Word>CASE</Word>
<Word>DATABASE</Word>
<Word>ELSE</Word>
<Word>AND</Word>
<Word>CHAR</Word>
<Word>DATA_BASE</Word>
<Word>ELSIF</Word>
<Word>ANY</Word>
<Word>CHAR_BASE</Word>
<Word>DATE</Word>
<Word>END</Word>
<Word>ARRAY</Word>
<Word>CHECK</Word>
<Word>DBA</Word>
<Word>ENTRY</Word>
<Word>ARRAYLEN</Word>
<Word>CLOSE</Word>
<Word>DEBUGOFF</Word>
<Word>EXCEPTION</Word>
<Word>AS</Word>
<Word>CLUSTER</Word>
<Word>DEBUGON</Word>
<Word>EXCEPTION_INIT</Word>
<Word>ASC</Word>
<Word>CLUSTERS</Word>
<Word>DECLARE</Word>
<Word>EXISTS</Word>
<Word>ASSERT</Word>
<Word>COLAUTH</Word>
<Word>DECIMAL</Word>
<Word>EXIT</Word>
<Word>ASSIGN</Word>
<Word>COLUMNS</Word>
<Word>DEFAULT</Word>
<Word>FALSE</Word>
<Word>AT</Word>
<Word>COMMIT</Word>
<Word>DEFINITION</Word>
<Word>FETCH</Word>
<Word>AUTHORIZATION</Word>
<Word>COMPRESS</Word>
<Word>DELAY</Word>
<Word>FLOAT</Word>
<Word>AVG</Word>
<Word>CONNECT</Word>
<Word>DELETE</Word>
<Word>FOR</Word>
<Word>BASE_TABLE</Word>
<Word>CONSTANT</Word>
<Word>DELTA</Word>
<Word>FORM</Word>
<Word>BEGIN</Word>
<Word>COUNT</Word>
<Word>DESC</Word>
<Word>FROM</Word>
<Word>FUNCTION</Word>
<Word>NEW</Word>
<Word>RELEASE</Word>
<Word>SUM</Word>
<Word>GENERIC</Word>
<Word>NEXTVAL</Word>
<Word>REMR</Word>
<Word>TABAUTH</Word>
<Word>GOTO</Word>
<Word>NOCOMPRESS</Word>
<Word>RENAME</Word>
<Word>TABLE</Word>
<Word>GRANT</Word>
<Word>NOT</Word>
<Word>RESOURCE</Word>
<Word>TABLES</Word>
<Word>GROUP</Word>
<Word>NULL</Word>
<Word>RETURN</Word>
<Word>TASK</Word>
<Word>HAVING</Word>
<Word>NUMBER</Word>
<Word>REVERSE</Word>
<Word>TERMINATE</Word>
<Word>IDENTIFIED</Word>
<Word>NUMBER_BASE</Word>
<Word>REVOKE</Word>
<Word>THEN</Word>
<Word>IF</Word>
<Word>OF</Word>
<Word>ROLLBACK</Word>
<Word>TO</Word>
<Word>IN</Word>
<Word>ON</Word>
<Word>ROWID</Word>
<Word>TRUE</Word>
<Word>INDEX</Word>
<Word>OPEN</Word>
<Word>ROWLABEL</Word>
<Word>TYPE</Word>
<Word>INDEXES</Word>
<Word>OPTION</Word>
<Word>ROWNUM</Word>
<Word>UNION</Word>
<Word>INDICATOR</Word>
<Word>OR</Word>
<Word>ROWTYPE</Word>
<Word>UNIQUE</Word>
<Word>INSERT</Word>
<Word>ORDER</Word>
<Word>RUN</Word>
<Word>UPDATE</Word>
<Word>INTEGER</Word>
<Word>OTHERS</Word>
<Word>SAVEPOINT</Word>
<Word>USE</Word>
<Word>INTERSECT</Word>
<Word>OUT</Word>
<Word>SCHEMA</Word>
<Word>VALUES</Word>
<Word>INTO</Word>
<Word>PACKAGE</Word>
<Word>SELECT</Word>
<Word>VARCHAR</Word>
<Word>IS</Word>
<Word>PARTITION</Word>
<Word>SEPARATE</Word>
<Word>VARCHAR2</Word>
<Word>LEVEL</Word>
<Word>PCTFREE</Word>
<Word>SET</Word>
<Word>VARIANCE</Word>
<Word>LIKE</Word>
<Word>POSITIVE</Word>
<Word>SIZE</Word>
<Word>VIEW</Word>
<Word>LIMITED</Word>
<Word>PRAGMA</Word>
<Word>SMALLINT</Word>
<Word>VIEWS</Word>
<Word>LOOP</Word>
<Word>PRIOR</Word>
<Word>SPACE</Word>
<Word>WHEN</Word>
<Word>MAX</Word>
<Word>PRIVATE</Word>
<Word>SQL</Word>
<Word>WHERE</Word>
<Word>MIN</Word>
<Word>PROCEDURE</Word>
<Word>SQLCODE</Word>
<Word>WHILE</Word>
<Word>MINUS</Word>
<Word>PUBLIC</Word>
<Word>SQLERRM</Word>
<Word>WITH</Word>
<Word>MLSLABEL</Word>
<Word>RAISE</Word>
<Word>START</Word>
<Word>WORK</Word>
<Word>MOD</Word>
<Word>RANGE</Word>
<Word>STATEMENT</Word>
<Word>XOR</Word>
<Word>MODE</Word>
<Word>REAL</Word>
<Word>STDDEV</Word>
<Word>NATURAL</Word>
<Word>RECORD</Word>
<Word>SUBTYPE</Word>
<Word>TRUNCATE</Word>
<Word>INSTALL</Word>
<Word>LOAD</Word>
</Keywords>
<Keywords color="Keyword1">
<Word>NOCOUNT</Word>
</Keywords>
<Keywords color="GoKeyword" >
<Word>GO</Word>
</Keywords>
<Rule color="ObjectReference1">([\d\w]+)\.([\d\w]+)\.([\d\w]+)</Rule>
<Rule color="ObjectReference">([\d\w]+)\.([\d\w]+)</Rule>
<Rule color="ObjectReferenceInBrackets1">([\d\w]+)\.([\d\w]+)\.([\d\w]+)</Rule>
<Rule color="ObjectReferenceInBrackets">\[([\d\w]+)\]\.\[([\d\w]+)\]\.\[([\d\w]+)\]</Rule>
<Rule color="ObjectReferenceInBrackets">\[([\d\w]+)\]\.\[([\d\w]+)\]</Rule>
<Rule color="Punctuation">
[?,.;()\[\]{}+\-/%*&lt;&gt;^+~!|&amp;]+
</Rule>
<Rule color="MethodCall">[\d\w_]+(?=(\s*\())</Rule>
<Rule color="Variable1">@@([\w]+)</Rule>
<Rule color="Variable">@([\w]+)</Rule>
<!-- Digits -->
<Rule color="Digits">
\b0[xX][0-9a-fA-F]+ # hex number
|
( \b\d+(\.[0-9]+)? #number with optional floating point
| \.[0-9]+ #or just starting with floating point
)
([eE][+-]?[0-9]+)? # optional exponent
</Rule>
</RuleSet>
</SyntaxDefinition>

View File

@ -28,31 +28,80 @@
Command="{Binding SideMenuSelectCommand}"
CommandParameter="DataListPage"
Header="已导入的数据" />
<hc:SideMenuItem
Command="{Binding SideMenuSelectCommand}"
CommandParameter="SqlQueryPage"
Header="查询" />
</hc:SideMenu>
<Frame
x:Name="MainFrame"
Grid.Column="1"
NavigationUIVisibility="Hidden" />
<hc:Dialog FontSize="16" Background="Transparent" x:Name="MessageDialog" Visibility="Collapsed" Grid.Column="0" Grid.ColumnSpan="2">
<Border BorderBrush="Gray" Background="White" BorderThickness="1" CornerRadius="5" Padding="0">
<hc:SimpleStackPanel ClipToBounds="True" Orientation="Vertical" Width="300" >
<Grid >
<hc:Dialog
x:Name="MessageDialog"
Grid.Column="0"
Grid.ColumnSpan="2"
Background="Transparent"
FontSize="16"
Visibility="Collapsed">
<Border
Padding="0"
Background="White"
BorderBrush="Gray"
BorderThickness="1"
CornerRadius="5">
<hc:SimpleStackPanel
Width="300"
ClipToBounds="True"
Orientation="Vertical">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*" MinHeight="100"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50" />
<RowDefinition Height="*" MinHeight="100" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Border CornerRadius="5" Grid.Row="0" Margin="0,0,0,10" Background="#d8d8d8">
<DockPanel Width="300" Grid.Row="0" HorizontalAlignment="Left" >
<TextBlock Margin="5,0" DockPanel.Dock="Left" Text="提示" VerticalAlignment="Center"/>
<Button Click="DialogCloseBtn_Click" Margin="5,0" HorizontalAlignment="Right" DockPanel.Dock="Right" Content="X"/>
<Border
Grid.Row="0"
Margin="0,0,0,10"
Background="#d8d8d8"
CornerRadius="5">
<DockPanel
Grid.Row="0"
Width="300"
HorizontalAlignment="Left">
<TextBlock
Margin="5,0"
VerticalAlignment="Center"
DockPanel.Dock="Left"
Text="提示" />
<Button
Margin="5,0"
HorizontalAlignment="Right"
Click="DialogCloseBtn_Click"
Content="X"
DockPanel.Dock="Right" />
</DockPanel>
</Border>
<TextBlock Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding ErrorMessage}"/>
<Border CornerRadius="5" Grid.Row="2" Margin="0,10,0,0" Background="#b2b2b2">
<TextBlock
Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding ErrorMessage}"
TextWrapping="Wrap" />
<Border
Grid.Row="2"
Margin="0,10,0,0"
Background="#b2b2b2"
CornerRadius="5">
<Grid>
<StackPanel Margin="5" VerticalAlignment="Center" HorizontalAlignment="Right">
<Button x:Name="DialogCloseBtn" Content="确定" Click="DialogCloseBtn_Click"/>
<StackPanel
Margin="5"
HorizontalAlignment="Right"
VerticalAlignment="Center">
<Button
x:Name="DialogCloseBtn"
Click="DialogCloseBtn_Click"
Content="确定" />
</StackPanel>
</Grid>
</Border>

View File

@ -28,17 +28,14 @@ public static class PasswordBoxHelper
{
return (string)dp.GetValue(PasswordProperty);
}
public static void SetPassword(DependencyObject dp, string value)
{
dp.SetValue(PasswordProperty, value);
}
private static bool GetIsUpdating(DependencyObject dp)
{
return (bool)dp.GetValue(IsUpdatingProperty);
}
private static void SetIsUpdating(DependencyObject dp, bool value)
{
dp.SetValue(IsUpdatingProperty, value);
@ -46,7 +43,7 @@ public static class PasswordBoxHelper
private static void OnPasswordPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
var passwordBox = sender as PasswordBox;
passwordBox.PasswordChanged -= PasswordChanged;
if (!(bool)GetIsUpdating(passwordBox))
{
@ -73,7 +70,7 @@ public static class PasswordBoxHelper
private static void PasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
var passwordBox = sender as PasswordBox;
SetIsUpdating(passwordBox, true);
SetPassword(passwordBox, passwordBox.Password);
SetIsUpdating(passwordBox, false);

View File

@ -3,6 +3,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:local="clr-namespace:ExcelHelper.Views.Pages"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="DataListPage"

View File

@ -189,7 +189,6 @@
<Label Content="地址:"/>
<hc:TextBox MinWidth="150" Text="{Binding DbServer}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="端口:"/>
<hc:TextBox MinWidth="50" Text="{Binding DbPort}" />
@ -200,20 +199,16 @@
<Label Content="账号:"/>
<hc:TextBox MinWidth="100" Text="{Binding DbUser}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="密码:"/>
<PasswordBox MinWidth="100" utils:PasswordBoxHelper.Password="{Binding DbPassword, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="数据库:"/>
<hc:TextBox MinWidth="100" Text="{Binding DbName}" />
<Button Content="测试连接" Command="{Binding TestDbConnCommand}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="导入后的数据表名称:"/>
<hc:TextBox MinWidth="100" Text="" />

View File

@ -0,0 +1,65 @@
<Page
x:Class="ExcelHelper.Views.Pages.SqlQueryPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:local="clr-namespace:ExcelHelper.Views.Pages"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="SqlQueryPage"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<StackPanel
Grid.Row="0"
HorizontalAlignment="Left"
Orientation="Horizontal">
<TextBlock
Margin="10"
VerticalAlignment="Center"
Text="储存类型:" />
<hc:ComboBox
x:Name="SqlQueryComboBox"
Width="200"
Margin="10"
DisplayMemberPath="Name"
SelectedValuePath="Sql" />
<Button
x:Name="ExecuteButton"
Margin="10"
VerticalAlignment="Center"
Click="ExecuteButton_Click"
Content="执行" />
</StackPanel>
<avalonEdit:TextEditor
Name="TextEditor"
Grid.Row="1"
MinHeight="200"
VerticalAlignment="Stretch"
FontFamily="Consolas"
FontSize="14pt"
LineNumbersForeground="Black"
ShowLineNumbers="True"
SyntaxHighlighting="SQL" />
<DataGrid
x:Name="ResultDataGrid"
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Width="Auto"
Margin="5"
HorizontalAlignment="Left"
hc:DataGridAttach.CanUnselectAllWithBlankArea="True" />
</Grid>
</Page>

View File

@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using DuckDB.NET.Data;
using ICSharpCode.AvalonEdit.Highlighting.Xshd;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit;
namespace ExcelHelper.Views.Pages
{
/// <summary>
/// SqlQueryPage.xaml 的交互逻辑
/// </summary>
public partial class SqlQueryPage : Page,IView
{
public SqlQueryPage()
{
InitializeComponent();
// 加载 HL/SQL.xshd
using (var reader = new System.Xml.XmlTextReader("HL/SQL.xshd"))
{
var xshd = HighlightingLoader.LoadXshd(reader);
var highlighting = HighlightingLoader.Load(xshd, HighlightingManager.Instance);
TextEditor.SyntaxHighlighting = highlighting;
}
}
// 替换 DuckDBDataAdapter 使用 DataTable.Load 方法
private void ExecuteButton_Click(object sender, RoutedEventArgs e)
{
// 获取 SQL 查询文本
string sqlQuery = TextEditor.Text;
// 创建 DuckDB 连接
using (var connection = new DuckDBConnection("DataSource=:memory:"))
{
connection.Open();
try
{
// 执行用户的查询
using (var command = connection.CreateCommand())
{
command.CommandText = sqlQuery;
using (var reader = command.ExecuteReader())
{
DataTable dataTable = new DataTable();
dataTable.Load(reader);
// 将结果绑定到 DataGrid
ResultDataGrid.ItemsSource = dataTable.DefaultView;
}
}
}
catch (Exception ex)
{
MessageBox.Show($"查询执行失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
connection.Close();
}
}
}
}

View File

@ -22,6 +22,7 @@ public partial class MainViewModel : ObservableObject, IViewModel
{
"ImportExcelPage" => typeof(ImportExcelPage),
"DataListPage" => typeof(DataListPage),
"SqlQueryPage" => typeof(SqlQueryPage),
_ => null
};
if (targetPage != null)