fix(pgwire): improve support for bind variables as function arguments#5999
fix(pgwire): improve support for bind variables as function arguments#5999bluestreak01 merged 54 commits intomasterfrom
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughAdds weak-dimensional array encoding and APIs in ColumnType; propagates weak-dims support and stricter dimensionality validation across array parsing, functions, casts, and PG wire binding; expands PostgreSQL OID mappings and PG bind handling; updates tests and compatibility runners for new array behavior and OIDs. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant PGWire
participant PGPipelineEntry
participant ColumnType
Client->>PGWire: Send bind param (array OID)
PGWire->>PGPipelineEntry: defineBindVariableType(oid)
PGPipelineEntry->>ColumnType: encodeArrayTypeWithWeakDims(baseType, false)
ColumnType-->>PGPipelineEntry: encoded array type (weak dims)
PGPipelineEntry-->>PGWire: bound type or error
PGWire-->>Client: bind accepted / error
sequenceDiagram
participant Parser
participant FunctionResolver
participant ColumnType
Parser->>FunctionResolver: resolve(function, argTypes)
alt param expects array
FunctionResolver->>ColumnType: decodeArrayElementType / decodeArrayDimensionality
ColumnType-->>FunctionResolver: elementType, nDims (or -1 for weak)
FunctionResolver->>Parser: pick overload or report array-aware error
else normal param
FunctionResolver->>Parser: pick overload
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🧪 Early access (Sonnet 4.5): enabledWe are currently testing the Sonnet 4.5 model, which is expected to improve code review quality. However, this model may lead to increased noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience. Note:
Comment |
|
@CodeRabbit please review |
🧩 Analysis chainI'll review the changes for you. Let me examine the modified files to understand the implementation details. 🏁 Script executed: Length of output: 949 Let me check the git status and examine the files directly to understand the changes: 🏁 Script executed: Length of output: 1022 Let me find the correct paths for these files and examine them: 🏁 Script executed: Length of output: 1768 Now let me examine the actual files to understand the implementation: 🏁 Script executed: Length of output: 266260 @bluestreak01 I'll review the changes that improve bind variable support as function arguments in pgwire. Let me examine the implementation across the four modified files. Overall AssessmentThis is a well-structured improvement to bind variable handling in PostgreSQL wire protocol functions. The changes address multiple pain points around type inference, validation, and client compatibility. Key Improvements1. Enhanced Type Resolution (FunctionParser.java)The function parser now properly handles // resolve previously UNDEFINED function types
for (int i = 0, n = undefinedVariables.size(); i < n; i++) {
final int pos = undefinedVariables.getQuick(i);
if (pos < candidateSigArgCount) {
// assign arguments based on the candidate function descriptor
int t = candidateDescriptor.getArgTypeWithFlags(pos);
final short sigArgType = FunctionFactoryDescriptor.toTypeTag(t);
final int argType;
if (FunctionFactoryDescriptor.isArray(t)) {
argType = ColumnType.encodeArrayType(sigArgType, 1);
} else {
argType = sigArgType;
}
args.getQuick(pos).assignType(argType, sqlExecutionContext.getBindVariableService());
}
}2. Improved PostgreSQL Type Mapping (PGOids.java)New array type support and better OID handling: public static int pgArrayToElementType(int pgOid) {
switch (pgOid) {
case PG_ARR_FLOAT8:
return PG_FLOAT8;
case PG_ARR_INT8:
return PG_INT8;
default:
return 0;
}
}3. Better Error Messages (PGPipelineEntry.java)The implementation now provides clearer error messages for type mismatches: case X_PG_NUMERIC:
throw CairoException.nonCritical().put("unsupported bind variable type NUMERIC (OID ").put(PG_NUMERIC).put(')');
case X_PG_ARR_NUMERIC:
throw CairoException.nonCritical().put("unsupported bind variable type NUMERIC[] (OID ").put(PG_ARR_NUMERIC).put(')');Areas of Concern1. Array Support LimitationThe array support is currently limited to // In ColumnType.java
arrayTypeSet.add(DOUBLE);
// Other types are commented outRecommendation: Consider documenting why only these two array types are supported or plan for extending support to other types mentioned in the PR objectives. 2. Complex Type Resolution LogicThe bind variable type resolution in // First we prioritize the types we received in the PARSE message
if (msgParseParameterTypeOIDs.size() > i) {
oid = msgParseParameterTypeOIDs.getQuick(i);
}
// if there was no type in the PARSE message, we use the type inferred by the compiler
final Function f = bindVariableService.getFunction(i);
int nativeType;
if (f != null) {
nativeType = f.getType();
if (oid == PG_UNSPECIFIED || oid == X_PG_VOID) {
oid = Numbers.bswap(PGOids.getTypeOid(nativeType));
}
}Recommendation: Consider adding more detailed comments explaining the precedence rules and edge cases. 3. Test CoverageWith 65.38% coverage, there are significant untested paths, particularly in:
Positive Aspects1. Comprehensive Type ValidationThe implementation properly validates unsupported types and provides clear error messages. 2. Client CompatibilityThe changes specifically address 3. Backward CompatibilityThe changes appear to maintain backward compatibility while fixing the identified issues. Minor Suggestions1. Code OrganizationConsider extracting some of the complex type resolution logic into separate methods for better readability: private int resolveBindVariableOid(int variableIndex, Function function) {
// Extract the OID resolution logic here
}2. ConstantsSome magic numbers could be extracted to named constants: private static final int BIND_VARIABLE_INDEX_OFFSET = 1; // for the -1 adjustmentConclusionThis is a solid improvement that addresses real compatibility issues with PostgreSQL drivers. The implementation is thorough and handles edge cases well. The main areas for improvement are expanding test coverage and potentially extending array type support. The changes should significantly improve the user experience when using bind variables with functions in QuestDB. The PR successfully addresses the stated objectives around type validation, client compatibility, and function signature resolution. ✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 3
🔭 Outside diff range comments (3)
core/src/main/java/io/questdb/cairo/ColumnType.java (1)
211-221: Assert-only validation risks invalid array encodings in productionBoth nDims bounds and element-type support checks are guarded by assertions. In production (asserts typically off), invalid values can slip through silently. Recommend adding fast runtime guards while keeping asserts for dev.
Apply this diff to add defensive checks without changing behavior for valid inputs:
- public static int encodeArrayType(short elemType, int nDims, boolean checkSupportedElementTypes) { - assert nDims >= 1 && nDims <= ARRAY_NDIMS_LIMIT : "nDims out of range: " + nDims; - assert !checkSupportedElementTypes || (isSupportedArrayElementType(elemType) || elemType == UNDEFINED) - : "not supported as array element type: " + nameOf(elemType); + public static int encodeArrayType(short elemType, int nDims, boolean checkSupportedElementTypes) { + assert nDims >= 1 && nDims <= ARRAY_NDIMS_LIMIT : "nDims out of range: " + nDims; + if (nDims < 1 || nDims > ARRAY_NDIMS_LIMIT) { + throw CairoException.critical(0).put("nDims out of range: ").put(nDims); + } + assert !checkSupportedElementTypes || (isSupportedArrayElementType(elemType) || elemType == UNDEFINED) + : "not supported as array element type: " + nameOf(elemType); + if (checkSupportedElementTypes && !(isSupportedArrayElementType(elemType) || elemType == UNDEFINED)) { + throw CairoException.critical(0).put("not supported as array element type: ").put(nameOf(elemType)); + } nDims--; // 0 == one dimension return (nDims & ARRAY_NDIMS_FIELD_MASK) << ARRAY_NDIMS_FIELD_POS | (elemType & ARRAY_ELEMTYPE_FIELD_MASK) << ARRAY_ELEMTYPE_FIELD_POS | ARRAY; }core/src/main/java/io/questdb/cutlass/pgwire/PGOids.java (2)
250-273: Add PG_UUID → PG_ARR_UUID mapping in pgToArrayOid (PGOids.java)Short: PGOids already defines PG_UUID and PG_ARR_UUID and PGPipelineEntry handles X_PG_ARR_UUID, but pgToArrayOid (core/src/main/java/io/questdb/cutlass/pgwire/PGOids.java) does not map PG_UUID. Add the case so typarray lookups (and PgTypeCatalogueCursor usage) advertise uuid[].
Files to touch
- core/src/main/java/io/questdb/cutlass/pgwire/PGOids.java — add PG_UUID case in pgToArrayOid (method around line ~250).
- core/src/main/java/io/questdb/griffin/engine/functions/catalogue/PgTypeCatalogueCursor.java — uses pgToArrayOid; will then report uuid[].
- core/src/main/java/io/questdb/cutlass/pgwire/PGPipelineEntry.java — already handles X_PG_ARR_UUID, no change needed.
Suggested diff
case PG_VARCHAR: return PG_ARR_VARCHAR; + case PG_UUID: + return PG_ARR_UUID; default: return 0;Note: Leave DATE unmapped in pgToArrayOid (QuestDB intentionally maps ColumnType.DATE to TIMESTAMP elsewhere).
231-240: pgArrayToElementType should mirror pgToArrayOid (add missing array→element mappings)pgToArrayOid returns array OIDs for BOOL, INT2, INT4, INT8, FLOAT4, FLOAT8, TIMESTAMP, TIMESTAMPTZ, VARCHAR, but pgArrayToElementType currently only recognizes FLOAT8[] and INT8[]. This asymmetry can break array detection (see PgTypeCatalogueCursor, PGPipelineEntry, PGNonNullBinaryArrayView).
- Fix: update core/src/main/java/io/questdb/cutlass/pgwire/PGOids.java -> pgArrayToElementType to include the missing cases:
public static int pgArrayToElementType(int pgOid) { switch (pgOid) { + case PG_ARR_BOOL: + return PG_BOOL; + case PG_ARR_INT2: + return PG_INT2; + case PG_ARR_INT4: + return PG_INT4; case PG_ARR_FLOAT8: return PG_FLOAT8; + case PG_ARR_FLOAT4: + return PG_FLOAT4; case PG_ARR_INT8: return PG_INT8; + case PG_ARR_TIMESTAMP: + return PG_TIMESTAMP; + case PG_ARR_TIMESTAMP_TZ: + return PG_TIMESTAMP_TZ; + case PG_ARR_VARCHAR: + return PG_VARCHAR; default: return 0; } }If the restriction to FLOAT8[] and INT8[] is intentional (only those arrays are expected in binary), please add a clarifying comment in the method instead of widening the mapping.
🧹 Nitpick comments (4)
core/src/main/java/io/questdb/cutlass/pgwire/PGPipelineEntry.java (1)
1312-1347: Comprehensive array bind-type definitions: good coverage, consider small cleanup
- Additions for BOOL, UUID, BYTEA, (VAR)CHAR, INT2/4/8, FLOAT4/8, DATE arrays are clear and consistent.
- Minor nit: these cases duplicate encodeArrayType(elem, 1, false). Consider a small helper to reduce repetition if you touch this again.
core/src/main/java/io/questdb/cairo/ColumnType.java (1)
516-527: Registering all N-D array names irrespective of support can mislead usersThis method registers array type names for all dimensions and for any base type passed in, while calling encodeArrayType(..., false) to bypass support checks. Given arrayTypeSet currently contains only a subset (e.g., DOUBLE), users can successfully reference names like "uuid[][]" that later fail at binding/casting.
- Either gate registration by support (for storage and/or PG semantics), or
- Keep the names but ensure any unsupported use fails with a precise, early error (ideally at parse time), and document the intent.
If you want to avoid surfacing unsupported multi-dimensional arrays for now, minimally constrain registration to 1-D for non-supported element types:
- for (int d = 1; d <= ARRAY_NDIMS_LIMIT; d++) { + final boolean supported = isSupportedArrayElementType(type); + final int maxDims = supported ? ARRAY_NDIMS_LIMIT : 1; + for (int d = 1; d <= maxDims; d++) { sink.put("[]"); int arrayType = encodeArrayType(type, d, false); String name = sink.toString(); typeNameMap.put(arrayType, name); nameTypeMap.put(name, arrayType); }Please confirm the intended scope: should multi-dimensional arrays be surfaced for all element types at the type-name level today, or only for element types that are actually supported in bindings/columns?
core/src/main/java/io/questdb/cutlass/pgwire/PGOids.java (2)
107-141: Reduce boilerplate for swapped OID constantsThese X_PG_* and X_PG_ARR_* constants are a straightforward byte swap. Consider extracting a private static helper to compute swapped OIDs to avoid duplication and maintenance burden.
Example:
private static int x(int oid) { return ((oid >> 24) & 0xff) | ((oid << 8) & 0xff0000) | ((oid >> 8) & 0xff00) | ((oid << 24) & 0xff000000); }Then:
public static final int X_PG_ARR_BOOL = x(PG_ARR_BOOL);etc.
321-323: TYPE_ARR_OIDS only maps DOUBLE[] and LONG[]; align with exposed capabilitiesDespite the broadened OID catalog, only DOUBLE and LONG have array OID mappings here. If the intent is to support additional array element types over PG wire soon (e.g., INT4[] or VARCHAR[]), add their mappings when ready. Otherwise, document this limitation to avoid confusion.
Would you like a follow-up patch that:
- Adds mappings for other safe/practical element types, and
- Synchronizes ColumnType.arrayTypeSet accordingly?
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these settings in your CodeRabbit configuration.
📒 Files selected for processing (7)
core/src/main/java/io/questdb/cairo/ColumnType.java(4 hunks)core/src/main/java/io/questdb/cutlass/pgwire/PGOids.java(5 hunks)core/src/main/java/io/questdb/cutlass/pgwire/PGPipelineEntry.java(2 hunks)core/src/main/java/io/questdb/griffin/FunctionParser.java(5 hunks)core/src/test/java/io/questdb/test/cutlass/pgwire/PGArraysTest.java(9 hunks)core/src/test/java/io/questdb/test/griffin/DBeaverTest.java(1 hunks)core/src/test/java/io/questdb/test/griffin/engine/functions/catalogue/PgTypeFunctionFactoryTest.java(2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (27)
- GitHub Check: New pull request (SelfHosted Other tests on linux-x64-zfs)
- GitHub Check: New pull request (SelfHosted Griffin And Fuzz tests on linux-x64-zfs)
- GitHub Check: New pull request (SelfHosted Other tests on linux-arm64)
- GitHub Check: New pull request (Hosted Running tests on windows-other)
- GitHub Check: New pull request (Hosted Running tests on windows-pgwire)
- GitHub Check: New pull request (Hosted Running tests on windows-cairo)
- GitHub Check: New pull request (Hosted Running tests on windows-fuzz2)
- GitHub Check: New pull request (Hosted Running tests on windows-fuzz1)
- GitHub Check: New pull request (Hosted Running tests on windows-griffin)
- GitHub Check: New pull request (Hosted Running tests on mac-other)
- GitHub Check: New pull request (Hosted Running tests on mac-pgwire)
- GitHub Check: New pull request (Hosted Running tests on mac-cairo-fuzz)
- GitHub Check: New pull request (Hosted Running tests on mac-cairo)
- GitHub Check: New pull request (SelfHosted Griffin And Fuzz tests Start ARM Agent)
- GitHub Check: New pull request (SelfHosted Griffin And Fuzz tests Start X64Zfs Agent)
- GitHub Check: New pull request (Hosted Running tests on mac-griffin)
- GitHub Check: New pull request (Rust Test and Lint on linux-jdk17)
- GitHub Check: New pull request (Hosted Running tests with cover on linux-other)
- GitHub Check: New pull request (Hosted Running tests with cover on linux-pgwire)
- GitHub Check: New pull request (Hosted Running tests with cover on linux-cairo)
- GitHub Check: New pull request (Hosted Running tests with cover on linux-fuzz)
- GitHub Check: New pull request (Hosted Running tests with cover on linux-griffin)
- GitHub Check: New pull request (SelfHosted Other tests Start X64Zfs Agent)
- GitHub Check: New pull request (SelfHosted Other tests Start ARM Agent)
- GitHub Check: New pull request (Check Changes Check changes)
- GitHub Check: build
- GitHub Check: build
🔇 Additional comments (19)
core/src/test/java/io/questdb/test/griffin/DBeaverTest.java (1)
165-179: Aligning typarray OIDs with PostgreSQL: LGTMUpdated typarray values for bool, int2/4/8, float4, varchar, and timestamp correctly reflect standard array OIDs (1000, 1005, 1007, 1016, 1021, 1015, 1115). This matches the broader array-type support and improves client compatibility in tooling like DBeaver.
core/src/test/java/io/questdb/test/griffin/engine/functions/catalogue/PgTypeFunctionFactoryTest.java (2)
37-45: Correct typarray OIDs for basic types: LGTMThe typarray column now exposes proper array OIDs for varchar, timestamp, float4, int2/4/8, and bool, which aligns with server-side array support and helps driver-side type resolution.
64-72: Prefixed PgType expectations updated: LGTMThe prefixed pg_type query expectations mirror the same OID corrections and look consistent.
core/src/test/java/io/questdb/test/cutlass/pgwire/PGArraysTest.java (8)
101-101: Use of float8 for array binding: LGTMSwitching to float8 aligns with introduced array OIDs and the array encoding path. This increases determinism across drivers.
156-157: Clarified assertion rationale: LGTMThe comment now clearly explains why error messages differ between text vs binary-mode array transmission.
173-175: Test for 1D->2D dimension mismatch: LGTMCreating a float8 1D array against a double[][] column is a good negative test for dimension validation.
178-184: Flexible assertion for driver/server mode variance: LGTMUsing assertContainsEither to accommodate mode-dependent error text avoids flaky tests while still validating correctness.
194-199: More dimensions than expected: LGTMMirrors the opposite mismatch scenario. Good coverage.
423-425: Empty array binding: LGTMEnsures empty array insertion works as expected in the new encoding path.
496-509: Jagged array rejection (variant 1): LGTMRobustly accounts for both text and binary protocol behaviors. This is important given pgjdbc’s jagged-array gap.
512-525: Jagged array rejection (variant 2): LGTMComplements the previous case; maintains deterministic checks.
core/src/main/java/io/questdb/cutlass/pgwire/PGPipelineEntry.java (2)
1290-1293: Array OID → native array mapping for timestamps: LGTMMapping X_PG_ARR_TIMESTAMP/_TZ to TIMESTAMP[] via ColumnType.encodeArrayType is consistent with the rest of the array handling and keeps timezone normalization on the server side.
1353-1371: Clear rejections for unsupported (array) types: LGTMExplicit non-critical exceptions for NUMERIC, TIME, INET, INTERVAL, JSONB and their arrays reduce ambiguity and improve client error messages.
core/src/main/java/io/questdb/griffin/FunctionParser.java (3)
794-840: Cast + undefined bind var defaults: sensible and pragmatic
- Avoiding arbitrary overload selection by assigning a default type for underdefined bind variables within cast() is a good call:
- textual targets → STRING
- numeric targets → DOUBLE
- array targets → assigned to the array type, optionally short-circuiting when it matches
This will significantly reduce “wrong overload” surprises with clients that send UNDEFINED types.
892-909: Array-aware overload resolution for UNDEFINED: LGTMTreating UNDEFINED on array parameters as a fuzzy match instead of a hard no-match broadens viable candidates, enabling later, precise type assignment. This matches the PR goal of bind-variable inference.
1054-1063: Assign array type to UNDEFINED based on candidate signature: LGTMThis ensures bind variables become e.g. DOUBLE[] instead of a plain scalar, allowing proper downstream validation and casting behavior.
core/src/main/java/io/questdb/cairo/ColumnType.java (1)
191-194: Good API: overload delegates to the stricter variantThe 2-arg overload cleanly routes through the 3-arg method, centralizing validation. No issues.
core/src/main/java/io/questdb/cutlass/pgwire/PGOids.java (2)
53-71: New array OID constants look correctThe OIDs for common array types (e.g., 1000 bool[], 1007 int4[], 1016 int8[], 1022 float8[], 1115 timestamp[], 1185 timestamptz[], 1015 varchar[]) match PostgreSQL values. Good coverage.
79-91: New scalar OIDs (INET, INTERVAL, JSONB, NUMERIC, TIME) are accurateThese constants align with PostgreSQL OID assignments. No issues. Ensure unsupported types (INET/INTERVAL and their arrays) are rejected upstream as per PR objectives.
Confirm that error paths in PGPipelineEntry or FunctionParser produce the intended messages for unsupported types referencing these OIDs.
core/src/main/java/io/questdb/cutlass/pgwire/PGPipelineEntry.java
Outdated
Show resolved
Hide resolved
1e140a8 to
7e45b4f
Compare
7e45b4f to
86089f3
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
core/src/main/java/io/questdb/griffin/engine/functions/array/DoubleArrayDotProductFunctionFactory.java (1)
158-165: Critical indexing bug in recursive path (uses left index for right array).At deepest dim, rightVal uses flatIndexLeft instead of flatIndexRight, producing wrong results for non‑vanilla arrays.
Apply this fix:
- double rightVal = right.getDouble(flatIndexLeft); + double rightVal = right.getDouble(flatIndexRight);Recommend adding a test that forces the non‑vanilla path (e.g., sliced/strided arrays with identical shapes) to catch this class of bugs.
♻️ Duplicate comments (3)
core/src/main/java/io/questdb/griffin/SqlCodeGenerator.java (1)
2225-2255: Replace asserts with runtime guards and enrich array-cast error messages.Asserts can be disabled in production. If non‑DOUBLE arrays reach this path, DOUBLE‑specific cast funcs may be constructed, risking incorrect behavior. Guard at runtime and include dimensionality in errors.
Apply this diff:
- assert ColumnType.decodeArrayElementType(fromType) == DOUBLE; - assert ColumnType.decodeArrayElementType(toType) == DOUBLE; + if (ColumnType.decodeArrayElementType(fromType) != DOUBLE + || ColumnType.decodeArrayElementType(toType) != DOUBLE) { + throw SqlException.unsupportedCast( + modelPosition, + castFromMetadata.getColumnName(i), + fromType, + toType + ); + } @@ - final int fromDims = ColumnType.decodeArrayDimensionality(fromType); - final int toDims = ColumnType.decodeArrayDimensionality(toType); - if (toDims == -1) { - throw SqlException.$(modelPosition, "cast to array bind variable type is not supported [column=") - .put(castFromMetadata.getColumnName(i)).put(']'); - } + final int fromDims = ColumnType.decodeArrayDimensionality(fromType); + final int toDims = ColumnType.decodeArrayDimensionality(toType); + if (toDims == -1) { + throw SqlException.$(modelPosition, "cast to array bind variable type is not supported [column=") + .put(castFromMetadata.getColumnName(i)) + .put(", toDims=").put(toDims).put(']'); + } @@ - if (fromDims > toDims) { - throw SqlException.$(modelPosition, "array cast to lower dimensionality is not supported [column=") - .put(castFromMetadata.getColumnName(i)).put(']'); - } + if (fromDims > toDims) { + throw SqlException.$(modelPosition, "array cast to lower dimensionality is not supported [column=") + .put(castFromMetadata.getColumnName(i)) + .put(", fromDims=").put(fromDims) + .put(", toDims=").put(toDims).put(']'); + } @@ - assert ColumnType.decodeArrayElementType(toType) == DOUBLE; - final int dims = ColumnType.decodeArrayDimensionality(toType); - if (dims == -1) { - throw SqlException.$(modelPosition, "cast to array bind variable type is not supported [column=") - .put(castFromMetadata.getColumnName(i)).put(']'); - } + if (ColumnType.decodeArrayElementType(toType) != DOUBLE) { + throw SqlException.unsupportedCast( + modelPosition, + castFromMetadata.getColumnName(i), + fromType, + toType + ); + } + final int dims = ColumnType.decodeArrayDimensionality(toType); + if (dims == -1) { + throw SqlException.$(modelPosition, "cast to array bind variable type is not supported [column=") + .put(castFromMetadata.getColumnName(i)) + .put(", toDims=").put(dims).put(']'); + }core/src/main/java/io/questdb/cairo/ColumnType.java (1)
759-771: Revisit LONG[] runtime support alignment.arrayTypeSet includes DOUBLE only; PG wire appears to map INT8[] (LONG[]) too in this PR. If LONG[] is intended to be supported end‑to‑end, add LONG here; otherwise confirm PG path rejects it consistently.
#!/bin/bash set -euo pipefail # Check runtime support set rg -n 'arrayTypeSet\.add\(' core/src/main/java/io/questdb/cairo/ColumnType.java -n # Check PG OID mapping for INT8[] rg -nP 'ARR.*(INT8|BIGINT)|PG_ARR_INT8|INT8\[\]' -n core/src/main/javacore/src/main/java/io/questdb/cutlass/line/tcp/ArrayBinaryFormatParser.java (1)
89-92: nDims read as unsigned: LGTM.This fixes spurious negatives from Unsafe.getByte.
Ensure a test exists for nDims=0xFF → ARRAY_TOO_MANY_DIMENSIONS:
#!/bin/bash set -euo pipefail rg -n 'ARRAY_TOO_MANY_DIMENSIONS|tooManyDims\(\)' core/src/test -C2
🧹 Nitpick comments (7)
core/src/main/java/io/questdb/griffin/engine/functions/finance/LevelTwoPriceArrayFunctionFactory.java (2)
101-107: Guard non‑positive targets (t <= 0).Prevents nonsensical pricing when target is zero/negative and avoids corner path returning the first price for t == 0.
Apply:
@Override public double getDouble(Record rec) { final double t = targetArg.getDouble(rec); + if (!(t > 0)) { + return Double.NaN; + } final ArrayView pricesArr = pricesArg.getArray(rec); final ArrayView sizesArr = sizesArg.getArray(rec); if (pricesArr.isNull() || sizesArr.isNull()) { return Double.NaN; }
110-112: Make the length‑mismatch error more actionable.Include actual lengths to speed up debugging.
Apply:
- throw CairoException.nonCritical().position(sizesArgPos) - .put("prices array length doesn't match sizes array length"); + throw CairoException.nonCritical().position(sizesArgPos) + .put("prices array length (").put(len) + .put(") doesn't match sizes array length (") + .put(sizesArr.getDimLen(0)).put(')');core/src/main/java/io/questdb/griffin/SqlCodeGenerator.java (1)
835-847: Add column context when UNION type widening fails due to array bind vars.getUnionCastType now throws, but the error surfaces without column info. Catch here and rethrow with the offending column name for better diagnostics.
Apply this diff:
- for (int i = 0; i < columnCount; i++) { - int typeA = typesA.getColumnType(i); - int typeB = typesB.getColumnType(i); - int targetType = getUnionCastType(typeA, typeB); - metadata.add(new TableColumnMetadata(typesA.getColumnName(i), targetType)); - } + for (int i = 0; i < columnCount; i++) { + final int typeA = typesA.getColumnType(i); + final int typeB = typesB.getColumnType(i); + final int targetType; + try { + targetType = getUnionCastType(typeA, typeB); + } catch (SqlException e) { + // Provide column context; position is unknown at this layer. + throw SqlException.$(0, "array bind variables are not supported in UNION queries [column=") + .put(typesA.getColumnName(i)).put(']'); + } + metadata.add(new TableColumnMetadata(typesA.getColumnName(i), targetType)); + }core/src/main/java/io/questdb/cairo/ColumnType.java (3)
223-233: Avoid assert‑only validation for prod paths.Asserts may be disabled; enforce checkSupportedElementTypes at runtime.
- assert !checkSupportedElementTypes || (isSupportedArrayElementType(elemType) || elemType == UNDEFINED) - : "not supported as array element type: " + nameOf(elemType); + if (checkSupportedElementTypes && !isSupportedArrayElementType(elemType) && elemType != UNDEFINED) { + throw CairoException.critical(0) + .put("not supported as array element type: ").put(nameOf(elemType)); + }
545-556: Precomputed array names: consider masking weak‑dims in nameOf for better diagnostics.nameOf(ARRAY with weak‑dims) currently returns "unknown". Mask the flag so logs/errors show familiar names.
public static String nameOf(int columnType) { + if (isArrayWithWeakDims(columnType)) { + columnType &= ~TYPE_FLAG_ARRAY_WEAK_DIMS; + } final int index = typeNameMap.keyIndex(columnType); if (index > -1) { return "unknown"; } return typeNameMap.valueAtQuick(index); }
892-906: Array type names for many element types: add a clarifying comment.Given only a subset is supported at runtime, add a one‑liner here stating these names are for error reporting/catalog friendliness.
- addArrayTypeName(sink, ColumnType.BOOLEAN); + // Note: names registered below are for error messages/catalog; runtime support is limited by arrayTypeSet. + addArrayTypeName(sink, ColumnType.BOOLEAN);core/src/main/java/io/questdb/cutlass/line/tcp/ArrayBinaryFormatParser.java (1)
76-86: Read elemType as unsigned for future‑proofing.Current tags fit in < 64, but mask defensively to avoid sign extension if new tags exceed 127.
- elemType = Unsafe.getUnsafe().getByte(addr); + elemType = (short) (Unsafe.getUnsafe().getByte(addr) & 0xFF);
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
core/src/main/java/io/questdb/cairo/ColumnType.java(9 hunks)core/src/main/java/io/questdb/cutlass/line/tcp/ArrayBinaryFormatParser.java(1 hunks)core/src/main/java/io/questdb/griffin/SqlCodeGenerator.java(6 hunks)core/src/main/java/io/questdb/griffin/engine/functions/array/DoubleArrayDotProductFunctionFactory.java(3 hunks)core/src/main/java/io/questdb/griffin/engine/functions/array/DoubleMatrixMultiplyFunctionFactory.java(3 hunks)core/src/main/java/io/questdb/griffin/engine/functions/finance/LevelTwoPriceArrayFunctionFactory.java(4 hunks)core/src/test/java/io/questdb/test/cairo/ArrayTest.java(6 hunks)core/src/test/java/io/questdb/test/cutlass/pgwire/PGJobContextTest.java(3 hunks)core/src/test/java/io/questdb/test/griffin/SqlCodeGeneratorTest.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- core/src/main/java/io/questdb/griffin/engine/functions/array/DoubleMatrixMultiplyFunctionFactory.java
- core/src/test/java/io/questdb/test/griffin/SqlCodeGeneratorTest.java
- core/src/test/java/io/questdb/test/cairo/ArrayTest.java
- core/src/test/java/io/questdb/test/cutlass/pgwire/PGJobContextTest.java
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-31T08:21:17.390Z
Learnt from: mtopolnik
PR: questdb/questdb#5997
File: core/src/main/java/io/questdb/griffin/engine/functions/array/DoubleArrayAccessFunctionFactory.java:127-131
Timestamp: 2025-07-31T08:21:17.390Z
Learning: In DoubleArrayAccessFunctionFactory.java, overflow validation for array index casting from long to int is performed in the validateIndexArgs() method rather than at each individual cast site. The validateIndexArgs() method is called early in newInstance() to validate all index arguments, making subsequent casts safe.
Applied to files:
core/src/main/java/io/questdb/griffin/SqlCodeGenerator.java
📚 Learning: 2025-08-04T09:16:27.366Z
Learnt from: jerrinot
PR: questdb/questdb#5996
File: core/src/test/java/io/questdb/test/cutlass/http/line/LineHttpSenderTest.java:350-356
Timestamp: 2025-08-04T09:16:27.366Z
Learning: In QuestDB ILP tests, maximum dimension values like `(1 << 28) - 1` are intentionally hard-coded instead of using constants like `ArrayView.DIM_MAX_LEN` to serve as "canaries" that will cause test failures if the API limits change, ensuring developers are aware of breaking changes to the ILP API.
Applied to files:
core/src/main/java/io/questdb/cutlass/line/tcp/ArrayBinaryFormatParser.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (28)
- GitHub Check: New pull request (SelfHosted Other tests on linux-arm64)
- GitHub Check: New pull request (SelfHosted Griffin And Fuzz tests on linux-x64-zfs)
- GitHub Check: New pull request (SelfHosted Other tests on linux-x64-zfs)
- GitHub Check: New pull request (SelfHosted Griffin And Fuzz tests on linux-arm64)
- GitHub Check: New pull request (Hosted Running tests on windows-other)
- GitHub Check: New pull request (Hosted Running tests on windows-pgwire)
- GitHub Check: New pull request (Hosted Running tests on windows-cairo)
- GitHub Check: New pull request (Hosted Running tests on windows-fuzz2)
- GitHub Check: New pull request (Hosted Running tests on windows-fuzz1)
- GitHub Check: New pull request (Hosted Running tests on windows-griffin)
- GitHub Check: New pull request (Hosted Running tests on mac-other)
- GitHub Check: New pull request (Hosted Running tests on mac-pgwire)
- GitHub Check: New pull request (Hosted Running tests on mac-cairo-fuzz)
- GitHub Check: New pull request (Hosted Running tests on mac-cairo)
- GitHub Check: New pull request (Rust Test and Lint on linux-jdk17)
- GitHub Check: New pull request (SelfHosted Griffin And Fuzz tests Start ARM Agent)
- GitHub Check: New pull request (SelfHosted Griffin And Fuzz tests Start X64Zfs Agent)
- GitHub Check: New pull request (SelfHosted Other tests Start ARM Agent)
- GitHub Check: New pull request (SelfHosted Other tests Start X64Zfs Agent)
- GitHub Check: New pull request (Hosted Running tests on mac-griffin)
- GitHub Check: New pull request (Hosted Running tests with cover on linux-other)
- GitHub Check: New pull request (Hosted Running tests with cover on linux-pgwire)
- GitHub Check: New pull request (Hosted Running tests with cover on linux-cairo)
- GitHub Check: New pull request (Hosted Running tests with cover on linux-fuzz)
- GitHub Check: New pull request (Hosted Running tests with cover on linux-griffin)
- GitHub Check: New pull request (Check Changes Check changes)
- GitHub Check: build
- GitHub Check: build
🔇 Additional comments (16)
core/src/main/java/io/questdb/griffin/engine/functions/array/DoubleArrayDotProductFunctionFactory.java (3)
33-33: Import addition is correct.Needed for the new init override. LGTM.
76-82: Pre-init dimensionality guard looks good.Allows weak-dim bind vars at construction time; strict check deferred to init. LGTM.
130-142: Init-time dimensionality check is appropriate.Good call to BinaryFunction.super.init(); enforcing dims equality here matches bind-var resolution flow.
Please confirm that removing isThreadSafe() doesn’t change concurrency semantics for this function. If the default isn’t true, reintroduce the override.
core/src/main/java/io/questdb/griffin/engine/functions/finance/LevelTwoPriceArrayFunctionFactory.java (6)
33-33: Import looks correct.Required for the new init(SymbolTableSource, SqlExecutionContext) override.
71-73: Argument/position wiring for pricesArg is correct.
82-83: Tracking pricesArgPos enables precise error reporting.
87-93: Constructor refactor LGTM.Fields are initialized in a clear order; no behavioral change.
167-167: toPlan ordering matches the new argument order (target, sizes, prices).
154-163: ```shell
#!/bin/bash
set -euo pipefailecho "1) Search for decodeArrayDimensionality usages..."
rg -n --hidden --no-ignore-vcs "decodeArrayDimensionality" -S || trueecho
echo "2) Locate ColumnType.* files"
rg -n --hidden --no-ignore-vcs --files "ColumnType.java" || true
rg -n --hidden --no-ignore-vcs "class ColumnType" -S || trueecho
echo "3) Show definition(s) containing decodeArrayDimensionality"
files=$(rg --hidden --no-ignore-vcs --files-with-matches "decodeArrayDimensionality" || true)
if [ -n "$files" ]; then
for f in $files; do
echo "---- $f ----"
rg -n --hidden --no-ignore-vcs "decodeArrayDimensionality" -n -C5 "$f" || true
sed -n '1,240p' "$f" | sed -n '1,240p'
echo
done
else
echo "No files contain decodeArrayDimensionality"
fiecho
echo "4) Search for existing helpers that handle weak/strong array dimensionality (isOneDimensional*, isArray*, abs patterns)..."
rg -n --hidden --no-ignore-vcs "isOneDimensional|isOneDimensionalArray|isArray1|isArray\(|arrayDimensionality|Math\.abs\([^)]+decodeArrayDimensionality" -S || trueecho
echo "5) Show other places where decodeArrayDimensionality is compared to 1 (to check current practice)"
rg -n --hidden --no-ignore-vcs "decodeArrayDimensionality\([^)]\)\s(==|!=|>|<|>=|<=)\s*-?1" -S || trueecho
echo "Done."</blockquote></details> <details> <summary>core/src/main/java/io/questdb/griffin/SqlCodeGenerator.java (2)</summary><blockquote> `310-310`: **LGTM on import.** No concerns with the added static import. --- `475-497`: **Fix typo in comment; keep dimensionality guard and surface column context** - Keep the hard error for unknown (-1) dimensionality. - Surface the column name at the call site (see widenSetMetadata) so the error has context. - Typo fix (apply below). ```diff - // if B is scalar then we coarse it to array of the same dimensionality as A is + // if B is scalar then we coerce it to an array with the same dimensionality as AVerify callers handle/declare SqlException at:
- core/src/main/java/io/questdb/griffin/SqlCodeGenerator.java:843
- core/src/test/java/io/questdb/test/griffin/SqlCodeGeneratorTest.java:8136-8137
core/src/main/java/io/questdb/cairo/ColumnType.java (5)
234-245: encodeArrayTypeWithWeakDims: LGTM.
286-293: isArrayWithWeakDims helper: LGTM.
557-563: Array cast tightened to equal dims and non‑weak dims: LGTM.
635-639: Undefined arrays include weak‑dims: LGTM.
169-182: Weak‑dims: returning -1 is fine; verify all callers handle it.decodeArrayDimensionality(...) returns -1 for weak dims; sandbox search here was inconclusive (ripgrep skipped files). Run locally and inspect any call sites or direct numeric comparisons:
rg -n --hidden --no-ignore -S 'decodeArrayDimensionality' -C3
rg -nP 'decodeArrayDimensionality([^)])\s(?:[><=!]=?)\s*-?\d+' -C3
git grep -n 'decodeArrayDimensionality'If any caller compares against a constant or assumes ≥1, update it to treat -1 as "unknown" or add an explicit guard.
...main/java/io/questdb/griffin/engine/functions/finance/LevelTwoPriceArrayFunctionFactory.java
Outdated
Show resolved
Hide resolved
…nt bind var dimensionalities
[PR Coverage check]😍 pass : 479 / 572 (83.74%) file detail
|
Fixes issues passing bind variables into function calls, such as
Multiple symptoms:
$1::float8[]cast in the SQLint8[]intoarray_position($1, field)would yield "expected DOUBLE but found ARRAY in parameter 1". This is completely confusing for the user and diverges from the documentation.tokio-postgres. The result would be complete impossibility of using bind variables with functions that have array args, even if bind variable is used for scalar parameters. The error would be something like "cannot convert UNKNOW to DOUBLE[]"Tested using AI with Rust:
Changes:
tokio-postgresbeing able to use bind variables.