FS-PIPE-RPC-1 | Partial | Implemented (22)- StructuredPipeline wire decoding
- database name, consistency selector (non-empty transaction; auto_commit_transaction only with new_transaction) and pipeline option (index_mode) checks
- finite database inputs, replace_with full and supported merge modes, find_nearest with Euclidean/cosine/dot-product measures and required local options; gRPC documents input supports finite read_time and existing transaction selectors, while new transactions and REST selectors remain unsupported; missing-only documents retain the selected execution time
- stage identification against the documented registry (collection, collection_group, database, documents, select, add_fields, remove_fields, where, sort, limit, offset, distinct, aggregate, find_nearest, sample, union, unnest, replace_with)
- input stage placement, arity, argument shape (paths, integers, boolean expressions, ascending / descending orderings, alias maps, vectors and distance measures, modes) and per-stage option checks (find_nearest limit / distance_field, unnest index_field; sample takes positional size and mode)
- write stages (update, delete) refused as FS-PIPE-WRITE-0
- unknown stages refused (FS_PIPE_UNSUPPORTED_STAGE), never skipped
- local latest-read input (collection, root or nested; collection_group; documents) followed by where, select, sort, add_fields, remove_fields, distinct, aggregate, offset and limit stages, each any number of times in any order, as FS-ENT-ORACLE O-6 (production, Enterprise, 2026-10-26, stage-order-relaxed) and O-7 answered them under both profiles. limit and offset take any non-negative integer, and a string argument and a negative limit are INVALID_ARGUMENT with the official texts ("The 'limit(...)' cannot have a negative value: -1", "Expected limit to be INTEGER_VALUE, but was STRING_VALUE."), as FS-ENT-ORACLE O-4 (production, Enterprise, 2026-10-20) answered under both profiles, with zero limit and offset recorded in O-5; the recorded boolean, double, null and empty-map limit refusals and negative and boolean offset refusals use the recorded errors; other strict request shapes remain gated; the argument checks of the stages (arity, aliases, empty maps) are production's recorded texts, a stage's own checks coming before an empty map; under strict only in the orders production recorded: an empty map or a later stage's check after a function or stage value fireemu does not check (production names a nonexistent function first, O-4 o4/stages#select-empty-then-unknown-function) is UNIMPLEMENTED
- local execution runs on the pipeline executor (three-valued evaluation: value, absent, error) over rows read at one read version; functions come from a static table and every other function name is refused (FS_PIPE_UNSUPPORTED_STAGE), never ignored
- operands: field references including nested paths (a.b; a path through a missing field or a non-map is absent, and an absent select value leaves its alias out), __name__ (the document reference, as FS-ENT-ORACLE O-1 (production, Enterprise, 2026-10-19) answers it), literals (as a select value too: values of every kind, vectors of doubles in their map form, nested arrays and maps, and map literals keyed by any name but an empty one ("Property names cannot be empty.") or a reserved one (__type__ aside), as FS-ENT-ORACLE O-1 to O-4 (production, Enterprise, 2026-10-19 and 2026-10-20) returned them, under both profiles; a vector with integer or NaN elements, no element, without value or with another key is UNIMPLEMENTED under strict when unrecorded; the recorded reserved and empty literal-key refusals, including __name__ and multi-key cases, are answered over REST and unambiguous gRPC, while three unordered multi-key gRPC cases remain UNIMPLEMENTED; the emulator profile refuses those vectors with the official emulator's texts before any other check of the literal; function options are ignored under the emulator profile, as the official emulator ignores them, and UNIMPLEMENTED under strict) and calls of the table's functions; document_id / collection_id / parent take a document reference (__name__, a reference field or a reference literal)
- select aliases are field paths under both profiles, as FS-ENT-ORACLE O-1, O-3 and O-4 (production, Enterprise, 2026-10-19 and 2026-10-20) and the official emulator answer them alike: a dotted alias nests, a quoted one unquotes, a reserved name below another field (a.__x__, a.__name__) is a nested key, and an empty alias, an invalid path (plain, quoted or dotted, of any length), a reserved first segment (__x__, __name__, quoted or not, with or without a child) or an alias inside another is INVALID_ARGUMENT FS_PIPE_INVALID with production's recorded text (an alias inside another with its ErrorInfo, reason OVERLAPPING_PROPERTY_PATHS); an alias whose segments, unquoted and joined with '.', reach 1500 UTF-8 bytes, whatever the characters, is INVALID_ARGUMENT with production's text and ErrorInfo (reason PROPERTY_PATH_TOO_LONG); an otherwise repeated path keeps fireemu's text; an alias and the same alias in quotes receive the recorded INTERNAL response under strict, while the emulator profile keeps its refusal; a select stage sets at most 64 fields, and 65 or more are INVALID_ARGUMENT FS_PIPE_INVALID with production's text ("The 'select(...)' stage is limited to 64 fields but this stage has 65.", FS-ENT-ORACLE O-2, production, Enterprise, 2026-10-20), and an empty select is INVALID_ARGUMENT ("The 'select(...)' stage requires at least one field.")
- sort orders by one or more {direction: ascending | descending, expression} maps (any expression, a literal included) in the canonical order of values across kinds; a missing value sorts as null (one value with it), the sort is stable, and descending reverses each comparison. aggregate({accumulators}, {groups}) computes count() (the rows), count(x) (rows where x is neither null nor missing), count_distinct(x), sum(x) and average(x) (over the numbers among the values: integers stay integers and an overflow is INVALID_ARGUMENT "Long overflow", any double makes the sum a double, the mean is a double, no number gives null), minimum(x) and maximum(x) (the least and greatest across kinds, null left out) per group; an aggregate without groups is one row even for an empty input and a grouped one has none; group keys treat null and a missing value alike and 1, 1.0, -0.0 and 0 alike and NaN with NaN, and a group shows its first member in the scan order as its key, as stored (a -0.0 stays -0). distinct({aliases}) keeps one row per combination of values. add_fields sets aliases read from the row as it was before the stage (a later alias does not see an earlier one, a missing value removes a top-level field and creates the parents of a nested alias as empty maps without setting it, a nested alias replaces a scalar parent; aliases that overlap are refused naming the later one) and keeps the name and times; remove_fields removes top-level and nested fields (a missing one is ignored; __name__ drops the name). collection_group(ancestor, id) reads every collection of that id at any depth, by parent document and then by CRC-32 of the id (the ancestor is validated as a document path and otherwise ignored; the empty reference is the root) and documents(paths) the existing documents among the paths, in name order. A select, distinct or aggregate leaves a row without name and times. and, or, not, xor and nor follow Kleene logic with null and missing as unknown, and an operand that is neither boolean nor unknown fails the call; exists, is_absent, is_error, type, is_type, array_contains, array_contains_all, array_contains_any, equal_any and not_equal_any answer as production did (FS-ENT-ORACLE O-1, O-6, O-7). Under strict, documents paths other than a collection and a document are unobserved. A pipeline reads its whole input before the first result, over gRPC as over REST, because production's scan order (CRC-32 of the document IDs) cannot be streamed from the name-ordered query pages; so collection | limit(1) reads every document of the collection (a bounded selection is a possible later optimization). The order of a collection group by parent document is generalized from the four parents of O-9 and O-9b (root, cg/a, cg/a/items/x, cg/b, oth/p) and is not observed beyond them. and, or and nor read their operands left to right, but an operand whose own evaluation fails is reported first wherever it stands (the emulator profile answers or(true, not(1)) with the failure of not); strict answers such a call beside a deciding operand as unobserved. exists and is_absent take a field reference and refuse any other operand, printed as the official emulator prints it. and, or, xor and nor read their operands left to right (the first deciding operand ends and, or and nor; a boolean is required before it) and take any number of operands; is_type answers every type name of the list, null for a missing value. The string functions concat, to_lower, to_upper, starts_with, ends_with, string_contains, char_length, substring, trim, ltrim, rtrim and split and the math functions add, subtract, multiply, divide, mod, abs, round, floor, ceil and pow follow FS-ENT-ORACLE O-8B (production, Enterprise, 2026-10-26): a null or missing argument gives null, an argument of the wrong type fails the request in production's words, integer arithmetic is checked (divide wraps int64 min by -1 and mod answers 0), and the argument-count refusals list the overloads. Under strict an argument shape no row covers is unobserved (a null beside a wrong type, bytes where only strings were shown, other white space for trim, a Greek sigma in a case mapping, a boolean string function as a whole where condition, ltrim and rtrim argument counts); the emulator profile evaluates by the same rules, which the official emulator 1.22.0 answered alike for all 247 non-regex rows (probed 2026-10-27; it stores a negative zero as 0.0). These stages and functions are answered with production's texts for their argument refusals, under strict only for the request shapes production refused
- a field equality is applied by the collection scan before documents are copied (the executor re-checks it), without index validation: Enterprise pipelines need no index, so an equality is answered without a single-field index or with an exempted one; this is Enterprise behavior pending production observation
- a where condition is a comparison, a field, another table function (document_id, collection_id, parent, array, map) or a literal: production keeps the row only when it is true and drops it for every other value, constant or missing one (FS-ENT-ORACLE O-1 to O-4 (production, Enterprise, 2026-10-19 and 2026-10-20), conditions and constants of every kind), under both profiles; filtering precedes projection, offset and limit
- comparisons equal, not_equal, less_than, less_than_or_equal, greater_than and greater_than_or_equal stand anywhere an expression stands (a where condition, a select value, a function argument), with fields and literals on either side, and follow the official emulator's order (firebase-tools 15.28.2) under both profiles: two absent operands are equal, one absent operand or operands of different kinds (NaN its own kind) are incomparable (not_equal true, the others false), and values of one kind follow the canonical order (vectors by length first, then element by element, as O-4 recorded with vectors of different lengths). FS-ENT-ORACLE O-1 to O-3 (production, Enterprise, 2026-10-19 and 2026-10-20) recorded production answering every probe the same way: every ordered pair of kinds, vectors included (each kind with itself, NaN and null included), and a missing field against a missing field and every kind, under all six comparisons, integers and doubles compared exactly as one kind (1 equals 1.0, -0.0 equals 0 and is not below 0.0, 2^53 + 1 is above the double 2^53, i64::MAX is below the double 2^63, -Infinity is below i64::MIN)
- array and map construction: array(...) writes null for a missing element and map(key, value, ...) leaves out a key whose value is missing, under both profiles (FS-ENT-ORACLE O-1 (production, Enterprise, 2026-10-19)); a key that is no literal is INVALID_ARGUMENT ("map keys must be constants/literals"), an integer or boolean key is INVALID_ARGUMENT naming its type (another type gets the same text under the emulator profile and UNIMPLEMENTED under strict, unrecorded), and a key repeats only with two present values (a missing value drops its entry first), under both profiles as O-3 and O-4 recorded; arrayValue / mapValue literals of literals are constants; a field or function anywhere inside an array or map literal, as a select value or an argument, is INVALID_ARGUMENT ("Value type is not supported: FIELD_REFERENCE_VALUE" or FUNCTION_VALUE, naming the first in document order) under both profiles, as production answered every probed composition (O-1, O-3, O-4); recorded reserved map-key shapes are refused under both profiles; other key shapes remain unverified
- document functions, as FS-ENT-ORACLE O-1, O-3 and O-4 (production, Enterprise, 2026-10-19 and 2026-10-20) answered under both profiles: a missing or null value is null; the parent of a top-level document is the documents root reference and the parent of the documents root is null; document_id and collection_id of the documents root are INVALID_ARGUMENT ("<function>(...) requires a non-empty path."); a collection at any depth is INVALID_ARGUMENT naming the reference and its length in UTF-16 units ("Document parent name \"<name>\" lacks \"/\" at index <length>."); a string, integer, map or boolean argument is INVALID_ARGUMENT with production's text and ErrorInfo (reason FUNCTION_WRONG_TYPE); recorded additional wrong-type REST shapes receive the corresponding FUNCTION_WRONG_TYPE error; unrecorded shapes remain gated, including unordered gRPC vector maps. A collection with a non-ASCII name is UNIMPLEMENTED under strict (production recorded ASCII names only); a malformed reference is UNIMPLEMENTED under both; results are streamed, so a run-time refusal may follow partial results, and rows after a used-up limit are not evaluated
- offset skips matching rows once before the existing limit and paged stream; zero, past-end and empty results preserve the existing finite response shape
- result documents: without a select each result is the source document (full name, create_time, update_time and fields) under both profiles, as the pipeline response message documents it and the official emulator answers; after a select a result is its fields only; every response carries execution_time, the read time of the pipeline's input snapshot
- a function call with the wrong number of arguments, an operand field path that does not parse and a missing operand value return INVALID_ARGUMENT FS_PIPE_INVALID with fireemu's own texts under the emulator profile and UNIMPLEMENTED FS_PIPE_UNSUPPORTED_STAGE under strict (production names the function's signature); function options are ignored under the emulator profile and UNIMPLEMENTED under strict; a stage's own argument count, argument shape and stage options (limit(), offset(), limit(1, 2), where(a, b), a three-argument aggregate) answer fireemu's stage-registry texts with INVALID_ARGUMENT under the emulator profile for collection, where, select, limit, offset, add_fields, distinct and aggregate, UNIMPLEMENTED for every other stage, and UNIMPLEMENTED under strict; under strict, an INVALID_ARGUMENT is answered only when the request has the shape of a request production refused in the FS-ENT-ORACLE recordings (conformance/fs-ent-pipeline-requests.json and fs-ent-pipeline-production.json), with that request's text, and is UNIMPLEMENTED FS_PIPE_UNSUPPORTED_STAGE otherwise (the shape keeps the stages in order, stage and function names, value kinds, array lengths and each map's entries in wire order, and replaces only field paths of plain names, map keys that are plain names and values of the same kind; a REST body keeps its key order, a decoded gRPC map keeps none, so over gRPC a refusal of a request with a map of two or more entries is UNIMPLEMENTED under strict; a call of constants that fails is refused before any row is read, the collection stage's reference is checked first (the rooted form /c(/d/c)*, the official texts otherwise; a string collection is refused), every non-string literal map key and a literal deeper than 20 levels are refused statically, every constant is checked against production's value limits and name rules before any row is read (a reference names a document, the database root or the documents root, checked in the official emulator's order: a missing projects, databases or documents part, an empty, . or .. id, a reserved __x__ id, a trailing /, more than 100 key path elements, an id over 1500 bytes; a string or bytes value of at most 1,048,487 bytes; a field path inside a literal (keys joined with .) of at most 1500 bytes; a REST bytesValue (or bytes_value) read as the official emulator reads it (trailing = dropped, one base64 alphabet, no other character, no lone character); a vector of at most 2048 dimensions), with the official texts under the emulator profile and UNIMPLEMENTED under strict, where a reference outside the request's project and database, the database root or the documents root, or a reference with an id such as ____ (reserved by production's documented pattern, accepted by the official emulator), is UNIMPLEMENTED as well; timestamp constants are cut to microseconds and a database-root reference is answered as its documents root; the collection stage refuses . and .. ids, reserved ids and ids over 1500 bytes with the official texts; a body that does not parse is UNIMPLEMENTED under strict and Payload isn't valid for request. under the emulator profile, and the REST transcoder's refusals of the body go through the same gate, while the emulator profile answers them with Payload isn't valid for request.); two select fields that fail when evaluated, a nested select alias of a missing value, a vector inside an array and a __type__ key beside other keys are UNIMPLEMENTED under strict; an empty array() or map(), a vector element that is infinite or a negative zero, a __type__ key that is no string, a reference literal that names no document, a document function of an argument type production named only for another function, a reserved, nested or shared alias of add_fields, distinct or aggregate, and fireemu's own nesting-limit text are UNIMPLEMENTED under strict; under the emulator profile those get the official emulator's texts (Field __type__ must be a string; founds <TYPE>., Document parent name ... lacks ..., Stage '<stage>': field name '<name>' is reserved., The nested field reference ..., The 'aggregate(...)' stage cannot have overlapping field names: [...], 'map' function must have an even number of arguments, map key must be of type STRING, given type is <TYPE> for a literal key before any row is read), with a function's argument count before its arguments and every select alias before its values, the aliases in a REST body's key order (an alias inside another names the later one) and otherwise in key order; the differential corpus conformance/fs-ent-pipeline-differential.json (official emulator answers) holds both profiles to these rules in tests; well-formed unsupported operand arrangements return UNIMPLEMENTED FS_PIPE_UNSUPPORTED_STAGE, with canonical pipeline context
- Enterprise local search immediately after collection is served by FS-TEXT-VAL-1 with the finite lexical-v1 development semantics and productionExact refusal; existing suffix stages execute after search without pruning indexed source fields
- The recorded O-8B and O-8C function cases run locally for array_length, array_first, array_last, array_get, map_get, map_set, map_remove, map_merge, map_keys, map_values, map_entries, regex_contains, regex_match, regex_find and regex_find_all. Regex uses a bounded Rust engine and, under strict, refuses constructs whose RE2 behavior is unverified; broader Unicode and regular-expression grammar compatibility are not established. Map key, value and entry output ordering beyond the recorded shapes is unobserved. Recorded O-8C cases also run locally for array_concat, array_reverse, array_slice, array_first_n, array_last_n, array_index_of, join, maximum_n, minimum_n, current_timestamp, timestamp_add, timestamp_subtract, timestamp_trunc, timestamp_diff, timestamp_extract, unix_micros_to_timestamp, unix_millis_to_timestamp, unix_seconds_to_timestamp, timestamp_to_unix_micros, timestamp_to_unix_millis and timestamp_to_unix_seconds. The request clock is sampled once and floored to microseconds; unrecorded custom GMT offsets and ambiguous named-zone boundaries are refused under strict. Some unrecorded argument shapes return UNIMPLEMENTED under strict.
Not implemented (3)- execution beyond the supported latest-read inputs and stages; gRPC consistency selectors beyond finite documents read_time and existing transaction reads, including new pipeline transactions; REST consistency selectors; unrecorded database, find_nearest and replace_with forms; database input with search or union; unrecorded union, sample and unnest shapes remain unverified
- functions beyond the comparisons, array and map constructors, array_length, array_first, array_last, array_get, map_get, map_set, map_remove, map_merge, map_keys, map_values, map_entries, regex_contains, regex_match, regex_find, regex_find_all, array_concat, array_reverse, array_slice, array_first_n, array_last_n, array_index_of, array_index_of_all, join, maximum_n, minimum_n, current_timestamp, timestamp_add, timestamp_subtract, timestamp_trunc, timestamp_diff, timestamp_extract, unix_micros_to_timestamp, unix_millis_to_timestamp, unix_seconds_to_timestamp, timestamp_to_unix_micros, timestamp_to_unix_millis, timestamp_to_unix_seconds, document_id, collection_id, parent, the logical, existence, type and membership functions, byte_length, string_index_of, string_repeat, string_replace_all, string_replace_one, string_reverse, vector_length, dot_product, euclidean_distance, cosine_distance, if_absent, if_null, if_error, conditional, switch_on and the other string and math functions named in the implemented list, and the aggregate functions count, count_distinct, sum, average, minimum and maximum; recorded cases of the named array functions are supported; other array shapes remain unverified; additional sqrt, exp, ln, log, log10 and zero-precision trunc shapes run in the emulator profile; strict admits finite recorded numeric and error shapes in their supported contexts; unrecorded numeric, domain, null, type and precedence cases remain gated; nonzero trunc precision and finite exponential subnormal outputs are unobserved; the "did you mean" hint of an unknown function name remains unsupported
- proxy / record-only modes
Notes (6)- REST geo points (every REST response and write, not only pipelines): a zero coordinate is left out and a left-out coordinate reads as zero, as production answered a pipeline literal (FS-ENT-ORACLE O-3) and a document write and read (O-4), following the proto3 JSON mapping
- Standard edition answers FAILED_PRECONDITION FS_PIPE_EDITION
- owner-only while rules are enforced
- REST POST {database}/documents:executePipeline (the Web SDK's firestore/lite transport) on Enterprise under both profiles: the JSON ExecutePipelineRequest (structuredPipeline, transaction / newTransaction / readTime, autoCommitTransaction, requestOptions accepted and ignored; a body database must equal the URL's) runs through the same validation, compilation and executor as gRPC over one snapshot; the answer is a JSON array that frames the results as production does (a small answer is one {results: [documents], executionTime} element; fireemu cuts a larger one at about 1 MiB; production cuts it into smaller units whose boundaries vary between reads, FS-ENT-ORACLE O-8A) and holds one {executionTime} element when there is none; refusals answer the one-element error array with the gRPC status, and a refusal answers no partial results; the strict profile's transcoder refuses unknown body fields as production's does; on Standard the strict profile answers production's FAILED_PRECONDITION and the emulator profile keeps its plain 404
- The finite local read subset is tested through tonic, with the Admin SDK over gRPC (firebase-admin 14.3.0 with @google-cloud/firestore 8.7.1 pipelines, tools/sdk-smoke/pipeline-admin.mjs) and over REST with the Web SDK (firebase 12.18.0 firestore/lite pipelines, tools/sdk-smoke/pipeline-lite.mjs) under both profiles. Enterprise production behavior is observed only as the FS-ENT-ORACLE recordings O-1 to O-9b took it (2026-10-19 to 2026-10-27: the stage grammar and its refusal texts, sort and stage order, where-context answers, aggregate, distinct, add_fields, remove_fields, collection_group, documents, the order of a read without a sort, the string, math, timestamp, map and array function families, union, unnest, sample, is_type over every type name and the response framing); recorded unnest and array subsequence, join and extrema cases run locally; finite collection-input union branches and documents/percent sample modes run locally; unrecorded shapes remain unverified; the implemented string, math, regex, timestamp, map and array functions cover recorded shapes, not every input or every member of those families. Not observed: error precedence on the Standard (default) database and for a non-owner caller, base64 decoding in createDocument, patch and batchWrite, non-ASCII document IDs and CRC-32 ties, and is_type of a vector. Production recorded HTTP 500 INTERNAL for an alias repeated in quotes and array_reverse() with no argument. Strict reproduces the recorded response for these cases, with a fixed console explanation that does not contain request values or credentials; a zero-argument array_reverse call with nonempty options retains its prior refusal. The emulator profile is unchanged. The corresponding gRPC production behavior is unobserved. Unsupported consistency selectors, metadata expressions and execution stages are refused, never silently ignored.
- known differences from FS-ENT-ORACLE O-1 to O-9b (production, Enterprise, 2026-10-19 to 2026-10-27) within this subset: production scans a collection in ascending CRC-32 of the document IDs (fireemu does too, so an unsorted answer, a limit or offset over it, the ties of a sort and the first member of a group follow it; CRC-32 ties and non-ASCII IDs are unobserved); the order of the groups of an aggregate is production's own and not reproduced (fireemu returns the order the groups were first met, so a limit, an offset or a sort with tied keys after the groups can differ); a limit after a sort cuts a group of rows tied on every sort key in an order the scan order does not explain; and a larger answer is cut into responses differently: production cuts a larger answer into units of 5 to 199 results (2 KB to 37 KB) whose boundaries differ between two reads of the same data (500 small documents came in 3 to 9 units, 5000 in 26 to 40; a document of 400 KB or more is a unit of its own and 100 KB documents come two to a unit), and put every recorded answer of 50 small documents or fewer in one response or element (FS-ENT-ORACLE O-8A); fireemu cuts at about 1 MiB of results; gRPC function expressions over 32 levels receive INVALID_ARGUMENT in both profiles; production recorded chain-32 success and chain-33 depth refusal. Strict gRPC maps the recorded chain-47 protobuf recursion refusal to INVALID_ARGUMENT; applying these diagnostics to other shapes is a generalization. The original chain-48/100 recorder failed before sending; later recordings on another named database returned code 3 and the generic invalid-argument message, matching local empty-database refusals for the same request structures, not seeded query execution; strict REST commit and runQuery reject recorded CR/LF and mixed alphabets and accept the proto field name `bytes_value`; the emulator profile remains permissive; the strict profile answers UNIMPLEMENTED, with a text that says production has not refused the shape, for some recorded shapes whose production answer is recorded and which the emulator profile reproduces; a stored -0.0 keeps its sign in production and in fireemu under both profiles, while the official emulator stores it as 0.0
|