This file provides guidance for AI coding agents working with the Apache Flink codebase.
- Java 11, 17 (default), or 21. Java 11 syntax must be used in all modules. Java 17 syntax (records, sealed classes, pattern matching) is only permitted in the
flink-tests-java17module. - Maven 3.9.16 (Maven wrapper
./mvnwincluded; prefer it) - Git
- Unix-like environment (Linux, macOS, WSL, Cygwin)
- Fast dev build:
./mvnw clean install -DskipTests -Dfast -Pskip-webui-build -T1C - Full build (Java 17 default):
./mvnw clean package -DskipTests -Djdk17 -Pjava17-target - Java 11:
./mvnw clean package -DskipTests -Djdk11 -Pjava11-target - Java 21:
./mvnw clean package -DskipTests -Djdk21 -Pjava21-target - Full build with tests:
./mvnw clean verify - Single module:
./mvnw clean package -DskipTests -pl flink-core-api - Single module with tests:
./mvnw clean verify -pl flink-core-api
- Single test class:
./mvnw -pl flink-core-api -Dtest=MemorySizeTest test - Single test method:
./mvnw -pl flink-core-api -Dtest=MemorySizeTest#testParseBytes test
- Format code (Java + Scala):
./mvnw spotless:apply - Check formatting:
./mvnw spotless:check - Checkstyle:
./mvnw checkstyle:check -T1C - Checkstyle config:
tools/maven/checkstyle.xml
Every module from the root pom.xml, organized by function. Flink provides three main user-facing APIs (recommended in this order: SQL, Table API, DataStream API) plus a newer DataStream v2 API.
flink-annotations— Stability annotations (@Public,@PublicEvolving,@Internal,@Experimental) and@VisibleForTestingflink-core-api— Core API interfaces (functions, state, types) shared by all APIsflink-core— Core implementation (type system, serialization, memory management, configuration)flink-runtime— Distributed runtime (JobManager, TaskManager, scheduling, network, state)flink-clients— CLI and client-side job submissionflink-rpc/— RPC frameworkflink-rpc-core— RPC interfacesflink-rpc-akka,flink-rpc-akka-loader— Pekko-based RPC implementation
flink-table/flink-sql-parser— SQL parser (extends Calcite SQL parser)flink-table-common— Shared types, descriptors, catalog interfacesflink-table-api-java— Table API for Javaflink-table-api-scala— Table API for Scalaflink-table-api-bridge-base,flink-table-api-java-bridge,flink-table-api-scala-bridge— Bridges between Table and DataStream APIsflink-table-api-java-uber— Uber JAR for Table APIflink-table-planner— SQL/Table query planning and optimization (Calcite-based)flink-table-planner-loader,flink-table-planner-loader-bundle— Classloader isolation for plannerflink-table-runtime— Runtime operators for Table/SQL queriesflink-table-calcite-bridge— Bridge to Apache Calciteflink-sql-gateway-api,flink-sql-gateway— SQL Gateway for remote SQL executionflink-sql-client— Interactive SQL CLIflink-sql-jdbc-driver,flink-sql-jdbc-driver-bundle— JDBC driver for SQL Gatewayflink-table-code-splitter— Code generation utilitiesflink-table-test-utils— Test utilities for Table/SQL
flink-streaming-java— DataStream API and stream processing operator implementations
flink-datastream-api— DataStream v2 API definitionsflink-datastream— DataStream v2 API implementation
flink-connectors/flink-connector-base— Base classes for source/sink connectorsflink-connector-files— Unified file system source and sinkflink-connector-datagen— DataGen source for testingflink-connector-datagen-test— Tests for DataGen connectorflink-hadoop-compatibility— Hadoop InputFormat/OutputFormat compatibilityflink-file-sink-common— Common file sink utilities
- Most connectors (Kafka, JDBC, Elasticsearch, etc.) live in separate repos under github.com/apache; see README.md for the full list
flink-formats/flink-json,flink-csv,flink-avro,flink-parquet,flink-orc,flink-protobuf— Serialization formatsflink-avro-confluent-registry— Avro with Confluent Schema Registryflink-sequence-file,flink-compress,flink-hadoop-bulk,flink-orc-nohive— Hadoop-related formatsflink-format-common— Shared format utilitiesflink-sql-json,flink-sql-csv,flink-sql-avro,flink-sql-parquet,flink-sql-orc,flink-sql-protobuf— SQL-layer format integrationsflink-sql-avro-confluent-registry— SQL-layer Avro with Confluent Schema Registry
flink-state-backends/flink-statebackend-rocksdb— RocksDB state backendflink-statebackend-forst— ForSt state backend (experimental; a fork of RocksDB)flink-statebackend-heap-spillable— Heap-based spillable state backendflink-statebackend-changelog— Changelog state backendflink-statebackend-common— Shared state backend utilities
flink-dstl/flink-dstl-dfs— State changelog storage (DFS-based persistent changelog for incremental checkpointing)
flink-filesystems/flink-hadoop-fs— Hadoop FileSystem abstractionflink-s3-fs-native,flink-s3-fs-hadoop,flink-s3-fs-presto,flink-s3-fs-base— S3 file systemsflink-oss-fs-hadoop— Alibaba OSSflink-azure-fs-hadoop— Azure Blob Storageflink-gs-fs-hadoop— Google Cloud Storageflink-fs-hadoop-shaded— Shaded Hadoop dependencies
flink-queryable-state/flink-queryable-state-runtime— Server-side queryable state serviceflink-queryable-state-client-java— Client for querying operator state from running jobs
flink-kubernetes— Kubernetes integrationflink-yarn— YARN integrationflink-dist,flink-dist-scala— Distribution packagingflink-container— Container entry-point and utilities for containerized deployments
flink-metrics/flink-metrics-core— Metrics API and core implementation- Reporter implementations:
flink-metrics-jmx,flink-metrics-prometheus,flink-metrics-datadog,flink-metrics-statsd,flink-metrics-graphite,flink-metrics-influxdb,flink-metrics-slf4j,flink-metrics-dropwizard,flink-metrics-otel
flink-libraries/flink-cep— Complex Event Processingflink-state-processing-api— Offline state access (savepoint reading/writing)
flink-models— AI model integration (sub-modules:flink-model-openai,flink-model-triton)flink-python— PyFlink (Python API)flink-runtime-web— Web UI for JobManager dashboardflink-external-resources— External resource management (e.g., GPU)docs/— Documentation content (Hugo site). This is where user-facing docs are written.flink-docs— Documentation build module (auto-generated config reference docs)flink-examples— Example programsflink-quickstart— Maven archetype for new projectsflink-walkthroughs— Tutorial walkthrough projects
flink-tests— Integration testsflink-end-to-end-tests— End-to-end testsflink-test-utils-parent— Test utility classesflink-yarn-tests— YARN-specific testsflink-fs-tests— FileSystem testsflink-architecture-tests— ArchUnit architectural boundary teststools/ci/flink-ci-tools— CI tooling
- Client submits jobs to the cluster. Submission paths include the CLI (
bin/flink runviaflink-clients), the SQL Client (bin/sql-client.shviaflink-sql-client), the SQL Gateway (flink-sql-gateway, also accessible via JDBC driver), the REST API (direct HTTP to JobManager), programmatic execution (StreamExecutionEnvironment.execute()orTableEnvironment.executeSql()), and PyFlink (flink-python, wraps the Java APIs). - JobManager (
flink-runtime) orchestrates execution: receives jobs, creates the execution graph, manages scheduling, coordinates checkpoints, and handles failover. Never runs user code directly. - TaskManager (
flink-runtime) executes the user's operators in task slots. Manages network buffers, state backends, and I/O. - Table Planner (
flink-table-planner) translates SQL/Table API programs into DataStream programs. The planner is loaded in a separate classloader (flink-table-planner-loader) to isolate Calcite dependencies. - Connectors communicate with external systems. Source connectors implement the
SourceAPI (FLIP-27); sinks implement theSinkAPI (packagesink2). Most connectors are externalized to separate repositories. - State Backends persist keyed state and operator state. RocksDB is the primary backend for production use.
- Checkpointing provides exactly-once guarantees. The JobManager coordinates barriers through the data stream; TaskManagers snapshot local state to a distributed file system.
Key separations:
- Planner vs Runtime: The table planner generates code and execution plans; the runtime executes them. Changes to planning logic live in
flink-table-planner; changes to runtime operators live inflink-table-runtimeorflink-streaming-java. - Codegen vs hand-written operators: Per-record expression logic (casts, projections, filters, function calls) is generated at planning time by cast rules in
flink-table-planner/.../functions/casting/and call generators inflink-table-planner/.../codegen/calls/, then compiled by Janino into the surrounding operator class. Operators with fixed structure (joins, aggregations, source/sink runtime) are hand-written Java inflink-table-runtimeorflink-streaming-java. New scalar functions usually only need aBuiltInFunctionDefinitionsentry plus aBuiltInScalarFunctionsubclass. New cast behaviour or a custom call shape needs a cast rule or call generator. - API vs Implementation: Public API surfaces (
flink-core-api,flink-datastream-api,flink-table-api-java) are separate from implementation modules. API stability annotations control what users can depend on. - ArchUnit enforcement:
flink-architecture-tests/contains ArchUnit tests that enforce module boundaries. New violations should be avoided; if unavoidable, follow the freeze procedure inflink-architecture-tests/README.md.
This section maps common types of Flink changes to the modules they touch and the verification they require.
A built-in function touches six modules. Each step names an example of that step done right. Don't copy a whole older function: most of them use the Calcite path, older test utilities, or test the same case at several levels.
- Check what other engines do. Compare the function with Spark, Snowflake, BigQuery, DuckDB, PostgreSQL and similar engines: its name, its NULL handling and the result type for each input type. Follow the closest one, and state the reason for any difference in the PR.
- Define it in
BuiltInFunctionDefinitionsinflink-table-common: name, kind, input and output type strategies, and how it runs, see step 3.- Write the name in upper case with underscores, such as
URL_DECODE, so that SQL and the Table API use the same name. - Reuse a strategy from
InputTypeStrategies,TypeStrategies,SpecificInputTypeStrategiesorSpecificTypeStrategiesbefore writing a new one. - When a new strategy would repeat code that other strategies share, extract a shared abstraction instead of copying it.
ValidationOnlyInputTypeStrategyis one: for a function that declares its arguments withstaticArguments(...), it fixes the argument count and signature, so a subclass only implementsinferInputTypesto validate. The strategies ofFROM_CHANGELOGandTO_CHANGELOGuse it. - Validate literal arguments in the input strategy, so that a malformed literal fails at planning time instead of on the first record.
- Register the function only here. Don't also add a field for it to
FlinkSqlOperatorTable: that class registers every field by reflection, and a second operator with the same name makes compiled plans that call the function fail to restore. - Every public class in
flink-table-commonandflink-table-api-java, nested classes included, needs a visibility annotation such as@Internal. ArchUnit checks this inTableApiRules.
- Write the name in upper case with underscores, such as
- Choose how it runs. The builder call in
BuiltInFunctionDefinitionsdecides:runtimeClass("...")is the default for scalar, table and process table functions. The class extendsBuiltInScalarFunction,BuiltInTableFunctionorBuiltInProcessTableFunctioninflink-table-runtime, and the generated code calls itsevalmethod directly. Precomputing a literal argument, such as parsing a path or compiling a pattern, is no reason for codegen. Do it once in the constructor fromSpecializedContext, asVariantGetFunctiondoes.runtimeProvided()on a scalar function means that the planner generates its code. Use it only when a runtime class can't do the job:- Performance, when calls can share work. Each call of a runtime class runs on its own, but generated code can reuse a result across the calls in one generated method.
PARSE_JSONandTRY_PARSE_JSONparse an input once for all calls on it, which a runtime class can't do. - The function reads the operator context, as
CURRENT_WATERMARKdoes. - The function evaluates an argument only when needed. A runtime class gets every argument already evaluated.
COALESCEandIF_NULLhave generated code for this, although they declareruntimeDeferred()andruntimeClass(...): a case inExprCodeGeneratortakes precedence over the builder call.
- Performance, when calls can share work. Each call of a runtime class runs on its own, but generated code can reuse a result across the calls in one generated method.
runtimeProvided()on an aggregate function means thatAggFunctionFactory.scalapicks the implementation, aBuiltInAggregateFunctioninflink-table-runtime. Every built-in aggregate takes this path, because the factory can choose a class from planning facts, such as a retracting variant when the input has updates. SeeBITMAP_BUILD_AGG.runtimeDeferred()maps the function to a CalciteSqlOperatorinFlinkSqlOperatorTable. Don't use it for a new function. It remains for older functions and for syntax that the SQL parser must handle, such asJSON_VALUE(... RETURNING ...).
- Implement it. See flink-table/flink-table-runtime/AGENTS.md for a runtime class and flink-table/flink-table-planner/AGENTS.md for codegen.
- A SQL NULL argument gives NULL, unless the function defines otherwise.
- A
TRY_variant returns NULL where the function would throw. Share one implementation, asTryVariantGetFunctiondoes by passing a flag toVariantGetFunction. - Throw a
ValidationExceptionfor an error found during planning, such as in a type strategy, and aTableRuntimeExceptionfor an error on a record. - Keep the per-record path fast, in a runtime class and in generated code alike.
evaland the generated call run once for every record, so a small cost there multiplies.- Do work that doesn't depend on the record once per query. A runtime class does it in its constructor, as
VariantGetFunctiondoes for a literal path. Generated code passes it in throughctx.addReusableObjector a reusable member, asStringCallGendoes for a literal pattern. - Cache what depends on an argument that is not a literal but rarely changes. Compare the argument with a copy of the last internal value, such as a
BinaryStringData, whoseequalscompares bytes. A cache hit then converts nothing. - Stay on internal data structures. Converting to a Java object per record, such as
StringData#toString(), allocates and copies. Don't allocate per record what can be reused, and avoid boxing. - A
TRY_variant doesn't build the error it would have thrown. On a rejected row,TRY_PARSE_JSONskips the message thatPARSE_JSONbuilds for its exception. - Don't log in the per-record path. A log line there can fill a TaskManager's disk.
- When two designs differ on the per-record path, measure both and put the numbers in the PR.
- Do work that doesn't depend on the record once per query. A runtime class does it in its constructor, as
- Add the Table API method in
flink-table-api-java: onBaseExpressionsfor a call on a value, such as$("v").variantGet("$.a"), or onExpressionsfor a call without one, such asuuidV4().ExpressionSerializationTestchecks the SQL that a Table API call prints, so add the call there. Keep the Javadoc to a few sentences: what the call returns, and when it returns NULL or fails. Refer to the SQL function for longer rules, such as a path syntax. - Add the PyFlink method. CI fails without it.
- A
BaseExpressionsmethod goes onExpressioninflink-python/pyflink/table/expression.py. AnExpressionsmethod goes intoflink-python/pyflink/table/expressions.py, with@PublicEvolving()like the functions around it. - Name it in snake_case after the Java method, such as
variantGetandvariant_get.test_expression_completeness.pyandtest_expressions_completeness.pyturn every Python name back into camelCase, and fail on a Java method without a match. - Call Java through the helpers in the same file, such as
_binary_op("variantGet"). Convert aDataTypeargument with_to_java_data_type. - Keep the docstring as short as the Javadoc. Write SQL NULL as NULL, not None, and give an example.
- In
flink-python/pyflink/table/tests/test_expression.py, assert the SQL that the expression prints, such as"VARIANT_GET(a, '$.x')". - List the method in
flink-python/docs/reference/pyflink.table/expressions.rst, under its category. No test checks this list.
- A
- Test each behavior once, with the cheapest test that covers it. An ITCase runs every case as a query on a mini cluster, so it costs the most CI time.
- Test the type strategies with a unit test that extends
InputTypeStrategiesTestBaseorTypeStrategiesTestBaseinflink-table-common, such asRegexpExtractInputTypeStrategyTest. Cover every argument type the strategy accepts, including lengths and precisions such asCHAR(33)andTIMESTAMP_LTZ(9), the inferred result type, and each validation error with its exact message. The ITCase doesn't repeat these cases. - Test a helper class, such as a path parser, with a plain unit test in its module, as
SimplePathTestdoes. - Add the ITCase cases to the existing ITCase of the function's category in
flink-table-planner, such asVariantFunctionsITCasefor a VARIANT function orCollectionFunctionsITCasefor an array function. Don't create a file for one function. - In the ITCase, cover what needs a running query: results, NULL inputs and runtime errors. Add a case for each input that the implementation handles differently, not for each type that the strategy accepts. Use
testResultto run the SQL and the Table API call against one expected value, andtestSqlRuntimeErrorwith the exact message. Call the Table API method, notcall("NAME"). - Don't add a case that another test already covers, directly or through a component the function reuses, such as a cast with its own tests.
- Test the type strategies with a unit test that extends
- Document it in
docs/data/sql_functions.yml: add a row withsql,tableanddescriptionto the function's category. Add the same row todocs/data/sql_functions_zh.yml, in English if you don't write Chinese. Describe what a user can observe: the arguments, the result, the NULL cases and the errors. Leave out implementation details, such as class names, code generation or caching. Check every statement against the type strategies, the runtime class and the tests, and take every result in an example from running its SQL.
- Define
ConfigOption<T>in the relevant config class (e.g.,ExecutionConfigOptions.javainflink-table-api-java) - Use
ConfigOptions.key("table.exec....")builder with type, default value, and description - Add
@Documentation.TableOptionannotation for auto-generated docs - Document in
docs/if user-facing - Verify: unit test for default value, ITCase for behavior change
- Involves
flink-table-runtime(operator),flink-table-planner(ExecNode, physical/logical rules), and tests across both - See flink-table/flink-table-planner/AGENTS.md and flink-table/flink-table-runtime/AGENTS.md for detailed development order and testing patterns
- Implement the
SourceAPI (flink-connector-base):SplitEnumerator,SourceReader,SourceSplit, serializers (SimpleVersionedSerializer) - Or implement the
SinkAPI (packagesink2) for sinks - Most new connectors go in separate repos under
github.com/apache, not in the main Flink repo - Verify: unit tests + ITCase with real or embedded external system
- Changes to
TypeSerializerrequire a correspondingTypeSerializerSnapshotfor migration - Bump version in
getCurrentVersion(), handle old versions inreadSnapshot() - Snapshot must have no-arg constructor for reflection-based deserialization
- Implement
resolveSchemaCompatibility()for upgrade paths - Verify: serializer snapshot migration tests, checkpoint restore tests across versions
- New user-facing API requires a voted FLIP (Flink Improvement Proposal); this applies to
@Public,@PublicEvolving, and@Experimentalsince users build against all three - Every user-facing API class and method must carry a stability annotation
- Changes to existing
@Publicor@PublicEvolvingAPI must maintain backward compatibility @InternalAPIs can be changed freely; users should not depend on them- Update JavaDoc on the changed class/method
- Add to release notes
- Verify: ArchUnit tests pass, no new architecture violations
- Format Java files with Spotless immediately after editing:
./mvnw spotless:apply. Uses google-java-format with AOSP style. - Scala formatting: Spotless + scalafmt (config at
.scalafmt.conf, maxColumn 100). - Checkstyle:
tools/maven/checkstyle.xml(version defined in rootpom.xmlascheckstyle.version). Some modules (flink-core, flink-optimizer, flink-runtime) are not covered by checkstyle enforcement, but conventions should still be followed. - No new Scala code. All Flink Scala APIs are deprecated per FLIP-265. Write all new code in Java.
- Apache License 2.0 header required on all new files (enforced by Apache Rat). Use an HTML comment for markdown files.
- API stability annotations: Every user-facing API class and method must have a stability annotation.
@Public(stable across minor releases),@PublicEvolving(may change in minor releases),@Experimental(may change at any time). These are all part of the public API surface that users build against.@Internalmarks APIs with no stability guarantees that users should not depend on. - Logging: Use parameterized log statements (SLF4J
{}placeholders), never string concatenation. - No Java serialization for new features (except internal RPC message transport).
- Use
finalfor variables and fields where applicable. - Comments: Do not add unnecessary comments that restate what the code does. Add comments that explain "the why" where relevant.
- Reuse existing code. Before implementing new utilities or abstractions, search for existing ones in the codebase. Prioritize architecture consistency and code reusability.
- Full code style guide: https://flink.apache.org/how-to-contribute/code-style-and-quality-preamble/
- Add tests for new behavior, covering success, failure, and edge cases.
- Use JUnit 5 + AssertJ assertions. Do not use JUnit 4 or Hamcrest in new test code.
- Prefer real test implementations over Mockito mocks where possible.
- Integration tests: Name classes with
ITCasesuffix (e.g.,MyFeatureITCase.java). - Red-green verification: For bug fixes, verify that new tests actually fail without the fix before confirming they pass with it.
- Test location mirrors source structure within each module.
- Follow the testing conventions at https://flink.apache.org/how-to-contribute/code-style-and-quality-common/#7-testing
[FLINK-XXXX][component] Descriptionwhere FLINK-XXXX is the JIRA issue number[hotfix][component] Descriptionfor typo fixes without JIRA- Each commit must have a meaningful message including the JIRA ID. If you don't know the ticket number, ask.
- Separate cleanup/refactoring from functional changes into distinct commits
- When AI tools were used: add
Generated-by: <Tool Name and Version>trailer per ASF generative tooling guidance
- Title format:
[FLINK-XXXX][component] Title of the pull request - A corresponding JIRA issue is required (except hotfixes for typos)
- Fill out the PR template completely but concisely: describe purpose, change log, testing approach, impact assessment
- Each PR should address exactly one issue
- Ensure
./mvnw clean verifypasses before opening a PR - Always push to your fork, not directly to
apache/flink - Rebase onto the latest target branch before submitting
- For user-visible behaviour changes, breaking changes, new SQL features, or new config options: fill in the Release Notes field on the JIRA ticket. The release manager consolidates these when cutting a release. The next version's
docs/content/release-notes/flink-X.Y.mdwill be generated based of the jira tickets, so make sure to fill them in properly.
- Disclose AI usage by checking the AI disclosure checkbox and uncommenting the
Generated-byline in the PR template - Add
Generated-by: <Tool Name and Version>to commit messages - Never add
Co-Authored-Bywith an AI agent as co-author; agents are assistants, not authors - You must be able to explain the design, code, and tests, debug them, and respond to review feedback substantively
- Reviewer-ready quality bar: the author owns PR quality. PRs that look AI-generated without author refinement (walls of unreviewed prose, scaffolding without behaviour, tests that do not exercise the change, padded commit messages) will be closed without review
When reviewing a PR or diff against this repo:
- Look for opportunities to simplify the code, scoped to the diff itself (not pre-existing code outside the change).
- Flag comments that are obvious (restate what the code already says) or overly verbose.
- In test code, look for potential flakiness — e.g.
Thread.sleepused outside a retry/poll loop, or similar timing-dependent, non-deterministic patterns. Where applicable, suggest clock injection (e.g. a manually-advancedClock/ManualClock) instead of relying on wall-clock time, or waiting for the actual condition in a loop with a timeout, for deterministic tests. - Check that each commit message conforms to Flink conventions: it must start with
[FLINK-XXXX]or[hotfix], and must specify a subsystem/component (e.g.[FLINK-XXXX][runtime] Description). - If a change introduces a new feature controlled by a config option/flag, check that the resolved state (enabled/disabled, and the effective value) is logged at INFO level when the feature initializes/activates.
- Consider whether a change should be hidden behind a feature flag, especially if it's non-trivial (touches core paths, changes default behavior, or is hard to reason about in isolation). This is mandatory if the change is risky (correctness, performance, backward-compatibility, or data-safety risk) and no flag/kill-switch already exists.
- For changes to configuration options, check if the corresponding documentation has been regenerated (this should be covered by tests but flagging it earlier speeds up development)
- Adding or changing
@Public,@PublicEvolving, or@Experimentalannotations (these are user-facing API commitments requiring a FLIP) - Large cross-module refactors
- New dependencies
- Changes to serialization formats (affects state compatibility)
- Changes to checkpoint/savepoint behavior
- Changes that could impact performance on hot paths (per-record processing, serialization, state access)
- Commit secrets, credentials, or tokens
- Push directly to
apache/flink; always work from your fork - Mix unrelated changes into one PR
- Use Java serialization for new features
- Edit generated files by hand when a generation workflow exists
- Use the legacy
SourceFunctionorSinkFunctioninterfaces for connectors; use theSourceAPI (FLIP-27) andSinkAPI (packagesink2) instead - Add
Co-Authored-Bywith an AI agent as co-author in commit messages; AI agents are assistants, not authors. UseGenerated-by: <Tool Name and Version>instead. - Suppress or bypass checkstyle rules (no
CHECKSTYLE:ON/CHECKSTYLE:OFFcomments, no adding entries totools/maven/suppressions.xml, no@SuppressWarnings). Fix the code to satisfy checkstyle instead. - Add, change, or remove classes outside the
org.apache.flink.*package (for example, classes copied from Calcite) - Modify
Parser.jj(Calcite's generated parser grammar; expected to be removed in future Calcite upgrades) - Use destructive git operations unless explicitly requested
- README.md — Build instructions and project overview
- DEVELOPMENT.md — IDE setup and development environment
- .github/CONTRIBUTING.md — Contribution process
- .github/PULL_REQUEST_TEMPLATE.md — PR checklist
- Code Style Guide — Detailed coding guidelines
- ASF Generative Tooling Guidance — AI tooling policy