From 3e470772c27ab20407645b8da12f154948bd6bc5 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 30 Nov 2025 22:03:37 +0200 Subject: [PATCH 1/4] Added a Claude AI configuration file and its initial project description --- .claude/settings.local.json | 17 ++++ CLAUDE.md | 170 ++++++++++++++++++++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 .claude/settings.local.json create mode 100644 CLAUDE.md diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 000000000..da9db0533 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,17 @@ +{ + "permissions": { + "allow": [ + "Bash(ctest --help:*)", + "WebFetch(domain:www.open-std.org)", + "Bash(python collapse_namespaces.py:*)", + "Bash(find:*)", + "Bash(cat:*)", + "Bash(done)", + "Bash(python fix_comment_alignment.py:*)", + "Bash(git checkout:*)", + "Bash(python:*)" + ], + "deny": [], + "ask": [] + } +} diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..1062db048 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,170 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## About sqlite_orm + +sqlite_orm is a header-only C++17 ORM library for SQLite. It provides type-safe database operations without raw SQL strings, using C++ template metaprogramming to map C++ structs to database tables. + +**Key characteristics:** +- Header-only library (main header: `include/sqlite_orm/sqlite_orm.h`) +- Requires C++17 minimum (supports C++20, C++23, C++26) +- Single dependency: libsqlite3 +- License: AGPL for open source, MIT after purchase + +## Build System + +### Building the project +```bash +# Configure with default C++17 +cmake -B build + +# Configure with specific C++ standard +cmake -B build -DSQLITE_ORM_ENABLE_CXX_20=ON +cmake -B build -DSQLITE_ORM_ENABLE_CXX_23=ON + +# Build +cmake --build build + +# Install (may need admin rights) +cmake --build build --target install +``` + +### Running tests +```bash +# Build tests (from project root) +cmake -B build -DBUILD_TESTING=ON +cmake --build build + +# Run all tests +cd build && ctest + +# Run unit tests directly (with more control) +./build/tests/unit_tests + +# Run specific test by name using Catch2 filter +./build/tests/unit_tests "Limits" +./build/tests/unit_tests "[ast_iterator]" +``` + +The test suite uses Catch2 framework. All test files are in `tests/` directory. Tests are organized using `TEST_CASE` and `SECTION` macros. + +### Building examples +```bash +cmake -B build -DBUILD_EXAMPLES=ON +cmake --build build +``` + +## Architecture Overview + +### Core Design Pattern +The library uses a **storage-centric architecture** with compile-time type safety through template metaprogramming: + +1. **Storage object** (`dev/storage.h`, `dev/storage_base.h`): The main interface that provides CRUD operations, query building, and schema management. Created via `make_storage()`. + +2. **Table definitions** (`dev/schema/table.h`): Tables are defined using `make_table()` which maps C++ structs to database schema. Columns are mapped using member pointers. + +3. **Type system** (`dev/type_traits.h`, `dev/type_printer.h`): Extensive compile-time type introspection to deduce types from member pointers and validate queries at compile time. + +4. **Statement serialization** (`dev/statement_serializer.h`, `dev/serializer_context.h`): Converts C++ expression objects into SQL strings. + +5. **Expression objects** (`dev/conditions.h`, `dev/core_functions.h`, `dev/ast/`): Type-safe representations of SQL operations (WHERE, JOIN, ORDER BY, etc.). + +### Key Implementation Files + +**Storage layer:** +- `dev/storage.h` - Main storage template with CRUD operations +- `dev/storage_base.h` - Base class with connection management, transactions, UDFs +- `dev/storage_impl.h` - Implementation details +- `dev/connection_holder.h` - RAII wrapper for sqlite3 connections + +**Schema definition:** +- `dev/schema/table.h` - Table definitions +- `dev/schema/column.h` - Column definitions and constraints +- `dev/schema/view.h` - View support +- `dev/schema/virtual_table.h` - Virtual table support +- `dev/schema/index.h` - Index support +- `dev/schema/triggers.h` - Trigger support + +**Query building:** +- `dev/conditions.h` - WHERE clause conditions (41K+ lines) +- `dev/core_functions.h` - SQL functions (82K+ lines) +- `dev/select_constraints.h` - SELECT modifiers (ORDER BY, LIMIT, etc.) +- `dev/ast/` - AST nodes for complex SQL constructs + +**Type binding:** +- `dev/statement_binder.h` - Binds C++ values to prepared statements +- `dev/row_extractor.h` - Extracts C++ objects from result rows +- `dev/field_printer.h` - Serializes field values + +**Utilities:** +- `dev/prepared_statement.h` - Prepared statement support +- `dev/ast_iterator.h` - Traverses expression ASTs +- `dev/transaction_guard.h` - RAII transaction guards +- `dev/cte_storage.h` - Common Table Expression support + +### Header Organization +The library has ~22,500 lines across the `dev/` directory headers. The main header `include/sqlite_orm/sqlite_orm.h` includes all necessary components. The `dev/` directory contains the actual implementation, organized by functionality. + +## Development Workflow + +### Code Style +- Follow existing code patterns in the codebase +- C++17 is the baseline; conditional compilation for C++20/23/26 features +- Use template metaprogramming for compile-time type safety +- Member pointers are used extensively for column mapping + +### Testing Requirements +- All changes must include tests in the `tests/` directory +- Use Test-Driven Development (TDD): write failing test first +- Tests use Catch2 framework with `TEST_CASE` and `SECTION` +- Ensure tests pass on multiple platforms (CI runs on Linux, Windows, macOS) + +### Compilation Considerations +- MSVC requires `/bigobj` flag for 64-bit builds (already configured) +- MSVC requires `/EHsc` for exception handling +- Clang/GCC: watch for `-Wreorder` warnings (treated as errors) +- Large template instantiations may cause long compile times + +### Pull Request Guidelines +Per `CONTRIBUTING.md`: +- Create GitHub issue for significant changes (not needed for typos/warnings) +- PR title must begin with issue number: `#9999 : description` +- Base PRs against `dev` branch (not `master`) +- Commit messages in English only +- Squash commits if adding/removing code within same PR +- All tests must pass on CI (Travis, AppVeyor, GitHub Actions) + +## Common Patterns + +### Creating a storage +```cpp +auto storage = make_storage("database.db", + make_table("users", + make_column("id", &User::id, primary_key().autoincrement()), + make_column("name", &User::name) + ) +); +storage.sync_schema(); // Creates/migrates schema +``` + +### Query expressions +Queries are built using expression objects that overload operators: +- `where(c(&User::id) == 5)` - type-safe WHERE clause +- `order_by(&User::name)` - ORDER BY +- `limit(10)` - LIMIT +- Composable: `where(...), order_by(...), limit(...)` + +### Member pointers for type safety +`&User::id` is used instead of string `"id"`. The library deduces the type and column name from the member pointer at compile time, preventing runtime SQL injection and type mismatches. + +## Important Notes + +- **Thread safety**: See `docs/thread-safety.md`. Storage objects are thread-safe by default since v1.10. +- **Schema sync**: `sync_schema()` attempts to preserve data but may drop tables if column constraints change significantly. Back up data before schema changes. +- **In-memory databases**: Use `:memory:` or `""` as filename. +- **Primary key requirement**: CRUD operations like `get()`, `update()`, `remove()` require a primary key column. +- **No raw SQL**: The library's design philosophy avoids raw SQL strings for type safety, but you can use `execute()` for raw queries if needed. + +## Current Branch +The current branch is `experimental/sql-view` (based on `dev`). Main branch is `master`, but active development happens on `dev`. From ca39c99e9470bf5a164cc1c7cfdadb602c576bfa Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 30 Nov 2025 22:04:06 +0200 Subject: [PATCH 2/4] Added a python script created by Claude to reformat misplaced comments --- third_party/fix_comment_alignment.py | 163 +++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 third_party/fix_comment_alignment.py diff --git a/third_party/fix_comment_alignment.py b/third_party/fix_comment_alignment.py new file mode 100644 index 000000000..95f72124f --- /dev/null +++ b/third_party/fix_comment_alignment.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +""" +Fix multi-line comment alignment in sqlite_orm dev/ headers. + +For a comment opening at column N: +- Continuation lines with `*` should have `*` at column N+1 +- Text on continuation lines should start at the next multiple of 4 from column 0 +- Continuation lines without `*` get an asterisk added +- The closing `*/` should be at column N+1 + +This script adds asterisks to continuation lines that don't have them, making all comments consistent. +""" + +import re +from pathlib import Path + + +def next_multiple_of_4(n): + """Return the next multiple of 4 that is greater than n.""" + return ((n // 4) + 1) * 4 + + +def fix_comment_alignment(file_path): + """Fix comment alignment in a single file.""" + with open(file_path, 'r', encoding='utf-8') as f: + lines = f.readlines() + + modified = False + result_lines = [] + in_comment = False + comment_start_col = 0 + + i = 0 + while i < len(lines): + line = lines[i] + + # Check if we're starting a multi-line comment + if not in_comment: + # Look for /* or /** at the start of a line (after whitespace) + match = re.match(r'^(\s*)/(\*+)(.*)$', line) + if match: + leading_spaces = match.group(1) + stars = match.group(2) + rest = match.group(3) + + # Check if it's a single-line comment + if rest.rstrip().endswith('*/'): + result_lines.append(line) + i += 1 + continue + + # Multi-line comment starts + in_comment = True + comment_start_col = len(leading_spaces) + result_lines.append(line) + i += 1 + continue + + # If we're in a comment, process continuation and closing lines + if in_comment: + # Expected indentation for continuation lines + star_col = comment_start_col + 1 # Where * should be + text_col = next_multiple_of_4(star_col) # Where text should start (next multiple of 4) + + # Check if this is the closing line (just spaces followed by */) + if re.match(r'^\s*\*/$', line): + current_indent = len(line) - len(line.lstrip()) + if current_indent != star_col: + fixed_line = ' ' * star_col + '*/\n' + result_lines.append(fixed_line) + modified = True + else: + result_lines.append(line) + in_comment = False + i += 1 + continue + + # Check for continuation line starting with * + match = re.match(r'^(\s+)\*\s*(.*)$', line) + if match: + current_indent = len(match.group(1)) + text_content = match.group(2) + + # Calculate expected format + spaces_after_star = text_col - star_col - 1 # -1 for the asterisk itself + expected_line = ' ' * star_col + '*' + ' ' * spaces_after_star + text_content + '\n' + + # Check if current line matches expected format + if line != expected_line: + result_lines.append(expected_line) + modified = True + else: + result_lines.append(line) + + # Check if this line contains the closing */ + if text_content.rstrip().endswith('/'): + in_comment = False + i += 1 + continue + + # Continuation line without asterisk + # Add asterisk and align properly, but skip preprocessor directives + if line.lstrip().startswith('#'): + # Preprocessor directive - this ends the comment + result_lines.append(line) + in_comment = False + i += 1 + continue + + if line.strip(): # Non-empty line + content = line.lstrip() + # Add asterisk and proper spacing + spaces_after_star = text_col - star_col - 1 + fixed_line = ' ' * star_col + '*' + ' ' * spaces_after_star + content + result_lines.append(fixed_line) + modified = True + else: + # Empty line in comment - keep as is + result_lines.append(line) + + i += 1 + continue + + # Normal line (not in comment) + result_lines.append(line) + i += 1 + + if modified: + new_content = ''.join(result_lines) + with open(file_path, 'w', encoding='utf-8') as f: + f.write(new_content) + return True + return False + + +def main(): + """Process all .h files in dev/ directory.""" + dev_dir = Path('dev') + + if not dev_dir.exists(): + print(f"Error: {dev_dir} directory not found") + return + + modified_files = [] + + # Find all .h files recursively + for h_file in sorted(dev_dir.rglob('*.h')): + if fix_comment_alignment(h_file): + modified_files.append(str(h_file)) + print(f"Fixed: {h_file}") + + print(f"\n{'='*60}") + print(f"Total files modified: {len(modified_files)}") + print(f"{'='*60}") + + if modified_files: + print("\nModified files:") + for f in modified_files: + print(f" - {f}") + + +if __name__ == '__main__': + main() From 80d4c212126418feba342376eb2696e5f45027d9 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 9 Dec 2025 20:23:01 +0200 Subject: [PATCH 3/4] Updated appveyor.yml to skip commits to Claude related files --- appveyor.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 20c5ad860..0dfd883be 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,7 +4,8 @@ version: "{build}" skip_branch_with_pr: true skip_commits: files: - - .git* + - .git/* + - .claude/* - .travis.yml - _config.yml - LICENSE From 2564fcc81acc0c9bccc27ab662604dd348e9068b Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 9 Dec 2025 20:29:52 +0200 Subject: [PATCH 4/4] Removed Claude's local settings file --- .claude/settings.local.json | 17 ----------------- .gitignore | 1 + 2 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 .claude/settings.local.json diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index da9db0533..000000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(ctest --help:*)", - "WebFetch(domain:www.open-std.org)", - "Bash(python collapse_namespaces.py:*)", - "Bash(find:*)", - "Bash(cat:*)", - "Bash(done)", - "Bash(python fix_comment_alignment.py:*)", - "Bash(git checkout:*)", - "Bash(python:*)" - ], - "deny": [], - "ask": [] - } -} diff --git a/.gitignore b/.gitignore index 129aed371..0d53a05af 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ examples/simple_neural_network.cpp cmake-build-debug/ +.claude/ .idea/ /compile