SqlNotebookScript is the scripting engine and database abstraction layer for SQL Notebook. It extends standard SQLite SQL with procedural programming constructs (variables, loops, conditionals, error handling) and provides import/export engines for CSV, Excel, text, and external databases.
ScriptParser
|
+-----------+-----------+
| |
TokenQueue SqliteParser
(from native (grammar-based
tokenizer) validation)
| |
+--------> AST <--------+
|
ScriptRunner
|
+-----------+-----------+
| | |
Notebook ScriptEnv ScriptOutput
(SQLite DB) (variables, (results,
control tables,
flow) text)
- Core/ - Database engine facade and native interop
- AdoModules/ - ADO.NET providers for external databases (SQL Server, PostgreSQL, MySQL, DuckDB, SQLite)
- GenericModules/ - Virtual table module providers for table-valued functions
- SqliteInterop/ - P/Invoke declarations for the native
sqlite3.dll
- Interpreter/ - Script parsing and execution
- Ast/ - Abstract syntax tree node definitions (28+ statement types)
- DataTables/ - Result set representations (memory-backed and disk-backed)
- ScalarFunctions/ - Custom SQL scalar functions (date, math, system, array, regex, download)
- TableFunctions/ - Custom SQL table-valued functions (list_files, read_file, list_xls_worksheets)
- Macros/ - SQL statement transformation macros
- Utils/ - Shared utilities (CSV, Excel, date/time, argument validation, blob handling)
The central class. Wraps a SQLite database with P/Invoke calls to the native sqlite3.dll. Provides the primary API for all database operations.
Key responsibilities:
- Lifecycle:
Notebook.New()creates a new in-memory notebook;Notebook.Open(filePath)opens an existing.sqlnbfile. The notebook isIDisposable. - Static init:
Notebook.InitSqlite()must be called once at startup. It loads the native DLL and SQLite extensions (crypto.dll,fuzzy.dll,stats.dll), and registers built-in functions (UUID, series). - Query execution:
Query(sql, args)returns aSimpleDataTable;Execute(sql, args)runs without results;QueryValue(sql, args)returns a single scalar. - Prepared statements:
Prepare(sql)returns aPreparedStatementfor repeated execution with parameter binding. - Tokenization:
Tokenize(sql)calls the nativeSxGetTokenexport to tokenize SQL input for the parser. - Persistence:
Save()andSaveAs(filePath)serialize the notebook. The file format is a SQLite database with the.sqlnbextension. - UserData: The
UserDataproperty (NotebookUserData) holds the notebook's pages and scripts as a serialized structure within the database. - Thread safety: Uses a static lock for all database operations.
Wraps a native SQLite prepared statement handle. Supports positional and named parameter binding, result iteration via row callbacks, and automatic parameter name extraction.
Enable importing data from external databases by registering SQLite virtual tables backed by ADO.NET connections:
| Provider | Class | ADO.NET Driver |
|---|---|---|
| SQL Server | SqlServerAdoModuleProvider |
Microsoft.Data.SqlClient |
| PostgreSQL | PostgreSqlAdoModuleProvider |
Npgsql |
| MySQL | MySqlAdoModuleProvider |
MySql.Data |
| DuckDB | DuckDBAdoModuleProvider |
DuckDB.NET |
| SQLite | SQLiteAdoModuleProvider |
Microsoft.Data.Sqlite |
Each provider registers a virtual table module (e.g., CREATE VIRTUAL TABLE t USING virtsqlserver(...)) that maps queries to the external database.
Enables custom table-valued functions to be exposed as SQLite virtual tables. Used to register list_files, read_file, and list_xls_worksheets.
The scripting language extends SQLite SQL with these procedural constructs:
| Statement | Description |
|---|---|
DECLARE @var [AS type] [= expr] |
Declare a local variable |
DECLARE PARAMETER @var [= default] |
Declare a script parameter |
SET @var = expr |
Assign a value to a variable |
IF expr BEGIN ... END [ELSE BEGIN ... END] |
Conditional execution |
WHILE expr BEGIN ... END |
Conditional loop |
FOR @i = start TO end [STEP step] BEGIN ... END |
Numeric iteration |
FOREACH (@col1, ...) IN (SELECT ...) BEGIN ... END |
Iterate over query results |
BREAK |
Exit innermost loop |
CONTINUE |
Skip to next loop iteration |
RETURN [value] |
Return from script |
EXECUTE script_name [@param = value, ...] |
Call another script |
PRINT expr |
Output text |
BEGIN TRY ... END TRY BEGIN CATCH ... END CATCH |
Exception handling |
THROW [message] |
Raise an error |
RETHROW |
Re-raise a caught error |
IMPORT CSV/TXT/XLS/DATABASE ... |
Import data from external sources |
EXPORT CSV/TXT ... |
Export data to files |
CREATE SCRIPT name AS 'sql' |
Create a stored script |
DROP SCRIPT/PAGE name |
Remove a notebook item |
SAVE ['filename'] |
Persist the notebook to disk |
Recursive descent parser that converts SQL text into an AST.
Parsing flow:
- Input SQL is tokenized using the native SQLite tokenizer (
Notebook.Tokenize), producing aTokenQueue. MacroProcessorapplies any registered macros to transform the token stream.ScriptParser.Parse()inspects the first token(s) to determine statement type (DECLARE, SET, IF, WHILE, etc.).- Custom statements are parsed directly by
ScriptParser. - Standard SQL statements are delegated to
SqliteParser, which validates them against the full SQLite grammar defined inSqliteGrammar.cs. - The result is an
Ast.Scriptcontaining aBlockofStmtnodes.
Contains the complete SQLite BNF grammar as production rules using combinator classes (Or, Opt, Tok, SubProd). The Matcher class performs recursive descent matching with backtracking. This is used to validate that standard SQL statements conform to SQLite syntax before execution.
Executes an Ast.Script by dispatching each statement to the appropriate handler based on its type. Maintains a ScriptEnv (execution environment) with:
- Variables: Case-insensitive dictionary of variable names to values.
- Control flow flags:
DidReturn,DidBreak,DidContinue. - Output accumulation:
ScriptOutputcollects data tables, text output, and scalar results. - Sub-script execution:
EXECUTEstatements resolve script names from the notebook's stored scripts and recursively invokeScriptRunner.
28+ statement node types, all inheriting from Ast.Stmt. Key nodes include:
DeclareStmt,SetStmt- Variable operationsIfStmt,WhileStmt,ForStmt,ForeachStmt- Control flowSqlStmt- Standard SQL (contains both raw text and aSqliteSyntaxProductionparse tree)ImportCsvStmt,ImportXlsStmt,ImportDatabaseStmt,ExportCsvStmt- Data I/OExecuteStmt- Script invocationTryCatchStmt,ThrowStmt- Error handling
Registered via reflection during Notebook.InitSqlite(). Each class provides one or more SQLite scalar functions:
- DateFunctions:
dateadd,datediff,datepart,datename,date_trunc,datefromparts,datetimefromparts,day,month,year,eomonth,getdate,getutcdate,now,isdate,to_date,to_datetime - MathFunctions:
round,sign,abs,ceiling,floor - SystemFunctions:
newid,host_name,user_name,read_file_text,isnumeric,choose,error_message - ArrayFunctions:
array,array_get,array_set,array_insert,array_remove,array_slice,array_concat,array_length - RegexpFunction: Regular expression matching
- DownloadFunction: HTTP GET requests returning file content
Exposed as virtual tables via GenericModuleProvider:
list_files(root_path, recursive)- File system enumeration with metadataread_file(path)- Read file contents into rowslist_xls_worksheets(file_path)- List Excel worksheet names
Each import type has a dedicated runner class in Interpreter/:
- ImportCsvStmtRunner: Parses CSV/TSV files with configurable delimiter, encoding, header handling, type conversion, and blank value treatment.
- ImportTxtStmtRunner: Line-by-line text import with line number and text columns.
- ImportXlsStmtRunner: Excel file import using
ExcelDataReader, supporting multiple worksheets and column type mapping. - ImportDatabaseStmtRunner: Connects to external databases via ADO.NET, supports schema/table/query selection with virtual table linking or data copy.
- ExportCsvStmtRunner: Exports query results, table data, or script output to CSV with configurable quoting, delimiter, and encoding.
- ExportTxtStmtRunner: Exports query results as formatted text.
- SimpleDataTable (abstract base): Column names and row access.
- MemorySimpleDataTable: Entire result set stored in memory. Used for small results.
- DiskSimpleDataTable: Large results spilled to temporary disk storage for memory efficiency.
- SimpleDataTableBuilder: Incrementally constructs result sets during query execution.
| Package | Purpose |
|---|---|
| DuckDB.NET.Data.Full | DuckDB database access |
| ExcelDataReader | Excel file (.xls/.xlsx) parsing |
| HtmlAgilityPack | HTML parsing (for documentation) |
| Microsoft.Data.SqlClient | SQL Server ADO.NET provider |
| Microsoft.Data.Sqlite | SQLite ADO.NET provider |
| MySql.Data | MySQL ADO.NET provider |
| Npgsql | PostgreSQL ADO.NET provider |
| SharpZipLib | ZIP archive handling |
- SQLite as the runtime: All data lives in SQLite. External databases are accessed through virtual tables, presenting a unified SQL interface.
- Native tokenizer: The parser reuses SQLite's own tokenizer (via P/Invoke to
SxGetToken) for token-level compatibility with SQLite SQL. - Grammar validation: Standard SQL is validated against the full SQLite grammar before execution, catching syntax errors early with helpful messages.
- Macro system:
MacroProcessorallows transparent SQL transformations (e.g., rewriting queries) without modifying the grammar itself. - Disk-backed results: Large result sets automatically spill to disk to prevent out-of-memory conditions.
- Extensibility by reflection: Custom functions, macros, and ADO modules are discovered and registered via reflection at initialization.