Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
bc9f0a9
feat(partiql): parse PartiQL statements and plan their key reads
LeeroyHannigan Nov 3, 2026
a213fba
feat(partiql): run ExecuteStatement, BatchExecuteStatement and Execut…
LeeroyHannigan Nov 3, 2026
1d458fc
test(partiql): add dual-target wire tests measured against DynamoDB
LeeroyHannigan Nov 3, 2026
0e10290
feat(partiql): authorize each statement with the action of its type
LeeroyHannigan Nov 3, 2026
ef59293
test(partiql): test the authorization of each statement
LeeroyHannigan Nov 3, 2026
8a3f8cd
style(partiql): clear the clippy pedantic warnings of the new code
LeeroyHannigan Nov 3, 2026
af0fac7
fix(partiql): size an UPDATE by its finished item, as the service does
LeeroyHannigan Nov 4, 2026
023404b
Merge origin/main into feat/partiql
LeeroyHannigan Nov 5, 2026
6c7e67c
fix(partiql): sort a batch by the statements that translate
LeeroyHannigan Nov 5, 2026
251490d
fix(partiql): count index units and failed writes in a batch
LeeroyHannigan Nov 5, 2026
26695e3
fix(partiql): check each transaction member in order
LeeroyHannigan Nov 5, 2026
4ba74b0
fix(partiql): honour the projection of a transaction read
LeeroyHannigan Nov 5, 2026
a80f39e
fix(partiql): refuse a statement of only whitespace as empty
LeeroyHannigan Nov 5, 2026
c8c51ed
fix(partiql): check ORDER BY on an index and refuse a path in its list
LeeroyHannigan Nov 5, 2026
095190c
fix(partiql): refuse a path into a key attribute in a write's WHERE
LeeroyHannigan Nov 5, 2026
94e6936
fix(partiql): read a string or a name in brackets as a map member
LeeroyHannigan Nov 5, 2026
b156d29
fix(partiql): read one value or path in parentheses as grouping
LeeroyHannigan Nov 5, 2026
eb92f2f
fix(partiql): refuse a negative zero decimal literal
LeeroyHannigan Nov 5, 2026
a0454c4
style(partiql): apply rustfmt to the PartiQL changes
LeeroyHannigan Nov 5, 2026
90aea5f
fix(partiql): end an operand at a token that does not go on with it
LeeroyHannigan Nov 5, 2026
58e1264
Merge the PartiQL parser fixes into feat/partiql
LeeroyHannigan Nov 5, 2026
c745a3c
fix(partiql): refuse an index key type ahead of a path into a key
LeeroyHannigan Nov 5, 2026
a27e698
Merge origin/main into feat/partiql
LeeroyHannigan Nov 6, 2026
f9255f1
fix(partiql): fail only the batch statement whose translation fails
LeeroyHannigan Nov 6, 2026
58f036e
fix(partiql): refuse empty bags and duplicate set members
LeeroyHannigan Nov 6, 2026
f5bebad
fix(partiql): refuse a path in brackets at its first step
LeeroyHannigan Nov 6, 2026
c312345
fix(partiql): fail the request for a parameter of the wrong JSON type
LeeroyHannigan Nov 6, 2026
c809dca
test(partiql): reach the statement's own entry in translate_write
LeeroyHannigan Nov 6, 2026
1c44453
fix(partiql): fail a batch for every fault of a statement translation
LeeroyHannigan Nov 6, 2026
82d6892
docs(partiql): describe the errors of update_refusal and insert_refusal
LeeroyHannigan Nov 6, 2026
8719285
fix(partiql): refuse a closing token where an INSERT item key starts
LeeroyHannigan Nov 6, 2026
1e59e51
refactor(partiql): drop the number walk the server no longer runs
LeeroyHannigan Nov 6, 2026
5853861
test(partiql): require HTTP 400 from a local server at the length limit
LeeroyHannigan Nov 6, 2026
0e4b4a8
test(partiql): pin the place of the arithmetic operand type check
LeeroyHannigan Nov 6, 2026
ba6f642
test(partiql): walk the deepest statements on a worker's stack
LeeroyHannigan Nov 6, 2026
5a677f2
Merge branch 'main' into feat/partiql
LeeroyHannigan Nov 6, 2026
54c4256
ci: give the two long integration jobs 60 minutes
LeeroyHannigan Nov 6, 2026
a157330
Merge origin/main into feat/partiql
LeeroyHannigan Nov 7, 2026
42ec18b
ci: raise four job bounds that the merged suites come near
LeeroyHannigan Nov 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/integration-cassandra.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,11 @@ jobs:

cassandra-pytest:
runs-on: extenddb_ubuntu-2404_4-core
timeout-minutes: 45
# With the PartiQL wire tests the job took up to 42 min 10 s on #411's CI,
# near the old 45-minute bound; on main it took 30 to 40 min. A run with a
# cold build cache should take about 44 min; 60 leaves room for a slow
# runner.
timeout-minutes: 60
services:
cassandra:
image: cassandra:4.1
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/integration-mongodb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ jobs:

mongodb-rust:
runs-on: extenddb_ubuntu-2404_4-core
timeout-minutes: 40
# The job took up to 38 min 42 s on main's CI in November 2026, near the
# old 40-minute bound; 55 leaves room for a slow runner.
timeout-minutes: 55
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
- uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # master
Expand Down
19 changes: 12 additions & 7 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ jobs:
# 2026 and this compiles Rust; move to 26 deliberately.
runs-on: extenddb_ubuntu-2404_4-core
# With the backup store and job suites it ends with (3 min 3 s of it), the
# job took 39 min 49 s on #740's CI, at the old 40-minute bound; 55 leaves
# room for a slow runner.
timeout-minutes: 55
# job took 39 min 49 s on #740's CI, at the old 40-minute bound. With the
# PartiQL wire tests it took 48 min 4 s on #411's CI, before #740. A run
# with both should take about 50 min; 70 leaves room for a slow runner.
timeout-minutes: 70
services:
postgres:
# pgvector's own image, which is postgres:16 plus the extension. The
Expand Down Expand Up @@ -214,9 +215,10 @@ jobs:
run-integration-sqlite:
runs-on: extenddb_ubuntu-2404_4-core
# With the backup store and job suites it ends with (4 min 16 s of it), the
# job took 40 min 31 s on #740's CI, past the old 40-minute bound; 55 leaves
# room for a slow runner.
timeout-minutes: 55
# job took 40 min 31 s on #740's CI, past the old 40-minute bound. With the
# PartiQL wire tests it took 43 min 27 s on #411's CI, before #740. 60
# leaves room for both and a slow runner.
timeout-minutes: 60
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0

Expand Down Expand Up @@ -526,7 +528,10 @@ jobs:

run-rust-integration:
runs-on: extenddb_ubuntu-2404_4-core
timeout-minutes: 40
# The job took 33 min 21 s on #740's merge queue run and 29 min 39 s on
# #411's CI, before #740. A run with both should take about 36 min; 50
# leaves room for a slow runner.
timeout-minutes: 50
services:
postgres:
image: pgvector/pgvector:pg16
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 30 additions & 2 deletions crates/auth/src/policy/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ pub struct RequestParams {
pub return_consumed_capacity: Option<String>,
/// The enclosing operation for batch/transact sub-operations.
pub enclosing_operation: Option<String>,
/// `dynamodb:FullTableScan` for a request that is not a scan
/// (`RequestContext::build` sets it to `true` for one). `None` leaves the
/// key absent; a PartiQL read by key sets `false`.
pub full_table_scan: Option<bool>,
/// The caller ARN (`aws:PrincipalArn`), when known.
pub principal_arn: Option<String>,
/// The caller's account id (`aws:PrincipalAccount`), when known.
Expand Down Expand Up @@ -301,7 +305,8 @@ impl RequestContext {
/// Build context for a `DynamoDB` operation.
///
/// `principal_tags` and `resource_tags` come from the identity and target
/// table respectively. `is_scan` should be true for Scan operations.
/// table respectively. `is_scan` should be true for Scan operations; for
/// any other request `params.full_table_scan` gives the key.
/// `params` carries operation-specific request parameters.
#[must_use]
pub fn build(
Expand All @@ -319,7 +324,11 @@ impl RequestContext {
select: params.select,
return_values: params.return_values,
return_consumed_capacity: params.return_consumed_capacity,
full_table_scan: if is_scan { Some(true) } else { None },
full_table_scan: if is_scan {
Some(true)
} else {
params.full_table_scan
},
enclosing_operation: params.enclosing_operation,
principal_arn: params.principal_arn,
principal_account: params.principal_account,
Expand Down Expand Up @@ -999,6 +1008,25 @@ mod tests {
);
}

#[test]
fn request_context_full_table_scan_set_by_the_request() {
// A PartiQL read by key carries "false" (measured 2026-11-03); a
// request that sets nothing leaves the key absent unless it is a scan.
let ctx = RequestContext::build(
HashMap::new(),
HashMap::new(),
false,
RequestParams {
full_table_scan: Some(false),
..Default::default()
},
);
assert_eq!(
ctx.resolve_key("dynamodb:FullTableScan"),
Some(vec!["false"])
);
}

#[test]
fn assume_role_context_external_id() {
let ctx = ar_ctx(HashMap::new(), Some("ext-123".to_owned()), HashMap::new());
Expand Down
6 changes: 6 additions & 0 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@ md-5 = { workspace = true }
sha2 = { workspace = true }
time = { workspace = true }
uuid = { workspace = true }

[features]
default = ["partiql"]
# PartiQL grammar, AST and query planning. Removable: with this off, the
# `partiql` module is not compiled and nothing else in core references it.
partiql = []
10 changes: 10 additions & 0 deletions crates/core/src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ pub enum DynamoDbError {
LimitExceededException(String),
#[error("{0}")]
ConditionalCheckFailedException(String, Option<Item>),
/// A PartiQL `INSERT` targeted a primary key that already exists.
///
/// `INSERT` is create-only, so a duplicate full key fails with this error
/// rather than `ConditionalCheckFailedException`. HTTP 400. The message is
/// always `Duplicate primary key exists in table`, measured on the service.
#[error("{0}")]
DuplicateItemException(String),
#[error("{message}")]
TransactionCanceledException {
/// Human-readable message.
Expand Down Expand Up @@ -157,6 +164,7 @@ impl DynamoDbError {
| Self::TableNotFoundException(_)
| Self::LimitExceededException(_)
| Self::ConditionalCheckFailedException(..)
| Self::DuplicateItemException(_)
| Self::TransactionCanceledException { .. }
| Self::IdempotentParameterMismatchException(_)
| Self::SerializationException(_)
Expand Down Expand Up @@ -204,6 +212,7 @@ impl DynamoDbError {
Self::TableNotFoundException(_) => "TableNotFoundException",
Self::LimitExceededException(_) => "LimitExceededException",
Self::ConditionalCheckFailedException(..) => "ConditionalCheckFailedException",
Self::DuplicateItemException(_) => "DuplicateItemException",
Self::TransactionCanceledException { .. } => "TransactionCanceledException",
Self::IdempotentParameterMismatchException(_) => "IdempotentParameterMismatchException",
Self::SerializationException(_) => "SerializationException",
Expand Down Expand Up @@ -280,6 +289,7 @@ impl DynamoDbError {
| Self::TableNotFoundException(m)
| Self::LimitExceededException(m)
| Self::ConditionalCheckFailedException(m, _)
| Self::DuplicateItemException(m)
| Self::IdempotentParameterMismatchException(m)
| Self::SerializationException(m)
| Self::UnknownOperationException(m)
Expand Down
6 changes: 6 additions & 0 deletions crates/core/src/expression/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ pub struct ExpressionMaps {
/// and was desugared into an update expression. The service sizes that
/// form against the item limit differently from an expression.
pub legacy_attribute_updates: bool,
/// Set by the engine for an update translated from a PartiQL `UPDATE`.
/// The service sizes that statement by its finished item alone, as it
/// sizes a transacted Update, and not by the statement as it sizes
/// `UpdateItem`.
pub finished_item_sizing: bool,
/// Pre-parsed `BigDecimal` values for numeric placeholders.
/// Populated eagerly by `new()` and refreshed by `pre_parse_numerics()`.
parsed_numerics: HashMap<String, bigdecimal::BigDecimal>,
Expand All @@ -45,6 +50,7 @@ impl ExpressionMaps {
names,
values,
legacy_attribute_updates: false,
finished_item_sizing: false,
parsed_numerics: HashMap::new(),
};
maps.pre_parse_numerics();
Expand Down
2 changes: 2 additions & 0 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub mod error;
pub mod expression;
pub mod limits;
pub mod metrics;
#[cfg(feature = "partiql")]
pub mod partiql;
pub mod serde_helpers;
pub mod settings_keys;
pub mod throttle;
Expand Down
Loading
Loading