Skip to content

Rewrite limitations section, minor fixes to unimplemented functions#427

Merged
Lorak-mmk merged 5 commits intoscylladb:masterfrom
Lorak-mmk:unimplemented-functions
Feb 25, 2026
Merged

Rewrite limitations section, minor fixes to unimplemented functions#427
Lorak-mmk merged 5 commits intoscylladb:masterfrom
Lorak-mmk:unimplemented-functions

Conversation

@Lorak-mmk
Copy link
Contributor

@Lorak-mmk Lorak-mmk commented Feb 25, 2026

This PR rewrites limitations section in README, to make sure it is complete and correct.
It also fixes some problems I found when doing that - see specific commits for more info.
Now that driver 1.0 is released, I believe it is important to have a comprehensive section about limitations - it will be useful for migrating users.

I used a LLM-coded script to find functions present in cassandra.h, but not in library (and vice-versa). Here it is, maybe it will be useful in the future:

#!/usr/bin/env python3
"""
Compare symbols declared in a C header file against symbols exported by a
shared library (.so).

Usage:
    python3 compare_header_lib.py <header.h> <library.so>

Requires: gcc (for preprocessing), nm (from binutils), pycparser (pip).
"""

import argparse
import os
import re
import subprocess
import sys

import pycparser
import pycparser.c_ast as c_ast


def extract_header_functions(header_path: str) -> set[str]:
    """Parse a C header and return the set of declared function names."""

    header_path = os.path.abspath(header_path)

    # Step 1: Preprocess with gcc.
    # -DCASS_STATIC makes CASS_EXPORT expand to nothing (no __attribute__).
    result = subprocess.run(
        ["gcc", "-E", "-DCASS_STATIC", header_path],
        capture_output=True,
        text=True,
    )
    if result.returncode != 0:
        print(f"gcc preprocessing failed:\n{result.stderr}", file=sys.stderr)
        sys.exit(1)

    # Step 2: Keep only lines originating from the target header.
    # The preprocessor emits markers like:  # 123 "/path/to/file.h" ...
    # We track which file we're in and discard everything from system headers.
    lines = result.stdout.split("\n")
    header_basename = os.path.basename(header_path)
    in_target = False
    filtered: list[str] = []

    # Provide stub typedefs for standard types that were stripped with the
    # system headers.  pycparser only needs them to be *some* valid type.
    filtered.extend(
        [
            "typedef long size_t;",
            "typedef int int8_t;",
            "typedef int int16_t;",
            "typedef int int32_t;",
            "typedef long int64_t;",
            "typedef unsigned uint8_t;",
            "typedef unsigned uint16_t;",
            "typedef unsigned uint32_t;",
            "typedef unsigned long uint64_t;",
        ]
    )

    for line in lines:
        # Detect preprocessor line markers.
        m = re.match(r'^#\s+\d+\s+"(.+?)"', line)
        if m:
            in_target = header_basename in os.path.basename(m.group(1))
            continue
        # Skip any remaining preprocessor directives (e.g. #pragma).
        if line.startswith("#"):
            continue
        if in_target:
            filtered.append(line)

    text = "\n".join(filtered)

    # Step 3: Strip any surviving __attribute__((...)) constructs.
    # CASS_DEPRECATED expands to  func __attribute__((deprecated))  which
    # lives in our header's scope.  We need a balanced-paren remover because
    # the inner content can contain nested parentheses.
    text = _remove_attribute(text)

    # Step 4: Parse with pycparser and collect function declaration names.
    parser = pycparser.CParser()
    try:
        ast = parser.parse(text)
    except pycparser.plyparser.ParseError as exc:
        print(f"pycparser failed to parse preprocessed header:\n{exc}", file=sys.stderr)
        sys.exit(1)

    functions: set[str] = set()
    for node in ast.ext:
        if isinstance(node, c_ast.Decl) and isinstance(node.type, c_ast.FuncDecl):
            functions.add(node.name)
    return functions


def _remove_attribute(text: str) -> str:
    """Remove all ``__attribute__((...))`` sequences from *text*,
    handling arbitrarily nested parentheses."""
    result: list[str] = []
    i = 0
    needle = "__attribute__"
    n = len(needle)
    while i < len(text):
        if text[i:i + n] == needle:
            i += n
            # Skip optional whitespace before the opening '('.
            while i < len(text) and text[i] in " \t\n":
                i += 1
            if i < len(text) and text[i] == "(":
                depth = 1
                i += 1
                while i < len(text) and depth > 0:
                    if text[i] == "(":
                        depth += 1
                    elif text[i] == ")":
                        depth -= 1
                    i += 1
            continue
        result.append(text[i])
        i += 1
    return "".join(result)


def extract_library_functions(lib_path: str) -> set[str]:
    """Return the set of exported function symbol names from a shared library."""
    result = subprocess.run(
        ["nm", "-D", "--defined-only", lib_path],
        capture_output=True,
        text=True,
    )
    if result.returncode != 0:
        print(f"nm failed:\n{result.stderr}", file=sys.stderr)
        sys.exit(1)

    functions: set[str] = set()
    for line in result.stdout.splitlines():
        parts = line.split()
        # nm output: <address> <type> <name>
        if len(parts) == 3 and parts[1] == "T":
            functions.add(parts[2])
    return functions


def main() -> None:
    parser = argparse.ArgumentParser(
        description="Compare function declarations in a C header against "
        "exported symbols in a shared library."
    )
    parser.add_argument("header", help="Path to the C header file (.h)")
    parser.add_argument("library", help="Path to the shared library (.so)")
    args = parser.parse_args()

    if not os.path.isfile(args.header):
        print(f"Header file not found: {args.header}", file=sys.stderr)
        sys.exit(1)
    if not os.path.isfile(args.library):
        print(f"Library file not found: {args.library}", file=sys.stderr)
        sys.exit(1)

    header_funcs = extract_header_functions(args.header)
    lib_funcs = extract_library_functions(args.library)

    in_header_only = sorted(header_funcs - lib_funcs)
    in_lib_only = sorted(lib_funcs - header_funcs)

    if not in_header_only and not in_lib_only:
        print("All symbols match.")
        sys.exit(0)

    if in_header_only:
        print(f"=== In header but NOT in library ({len(in_header_only)}) ===")
        for name in in_header_only:
            print(f"  {name}")
        print()

    if in_lib_only:
        print(f"=== In library but NOT in header ({len(in_lib_only)}) ===")
        for name in in_lib_only:
            print(f"  {name}")
        print()

    print(
        f"Summary: header declares {len(header_funcs)} functions, "
        f"library exports {len(lib_funcs)} functions, "
        f"{len(in_header_only)} only in header, "
        f"{len(in_lib_only)} only in library."
    )
    sys.exit(1)


if __name__ == "__main__":
    main()

Pre-review checklist

  • I have split my patch into logically separate commits.
  • All commit messages clearly explain what they change and why.
  • PR description sums up the changes and reasons why they should be introduced.
  • I have implemented Rust unit tests for the features/changes introduced.
  • I have enabled appropriate tests in Makefile in {SCYLLA,CASSANDRA}_(NO_VALGRIND_)TEST_FILTER.
  • I added appropriate Fixes: annotations to PR description.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the project’s “Limitations” documentation to better reflect the current state of the wrapper driver and adjusts the public/deleted C API surface to match that documentation.

Changes:

  • Rewrites the README limitations section with more structured grouping and rationale/links.
  • Removes the wrapper implementation and Rust API re-export for cass_cluster_set_queue_size_event (previously a no-op).
  • Reorganizes C header declarations by moving several “not implemented / deleted” APIs out of cassandra.h into cassandra_deleted_functions.h, and moving cass_cluster_set_use_hostname_resolution into cassandra.h.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
scylla-rust-wrapper/src/cluster.rs Removes the no-op cass_cluster_set_queue_size_event symbol from the Rust wrapper.
scylla-rust-wrapper/src/api.rs Stops re-exporting cass_cluster_set_queue_size_event from the Rust API module.
include/cassandra_deleted_functions.h Moves/collects additional “deleted/unimplemented” API declarations here; removes hostname-resolution declaration from this header.
include/cassandra.h Adds cass_cluster_set_use_hostname_resolution to the public header and removes several raw metadata/“custom” declarations.
README.md Replaces the old limitations table/list with an expanded, categorized limitations section and issue links.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@wprzytula wprzytula left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some typos.

This function was deprecated and no-op in cpp-driver. We removed it from
cassandra.h before, so it makes no sense to keep it.
We are not planning to implement those, or any other function related to
custom types. Others were already moved to unimplemented, this one was
forgotten.
Reasoning for removal was imo flawed because of misleading name. We
should make sure that we want to remove this.
Relevant issue: scylladb#426
We do not plan to implement them.
Now it describes all functions that were present in original
cassandra.h, but are not implemented in our driver. Description is split
into categories, and each category is described, with links to relevant
issue.
@Lorak-mmk Lorak-mmk force-pushed the unimplemented-functions branch 2 times, most recently from bff5997 to 7579adf Compare February 25, 2026 18:18
@Lorak-mmk
Copy link
Contributor Author

Fixed typos, rebased on master

@Lorak-mmk Lorak-mmk merged commit 8212465 into scylladb:master Feb 25, 2026
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants