Skip to content

Commit 197875f

Browse files
committed
feat(gb-8962): postgres pagination
1 parent 498adeb commit 197875f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+3927
-706
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/postgres/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "grafbase-postgres"
3-
version = "0.3.2"
3+
version = "0.3.3"
44
edition = "2024"
55
license = "Apache-2.0"
66

cli/postgres/src/args.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use clap::{Parser, Subcommand};
55
#[derive(Parser, Debug)]
66
#[command(name = "grafbase-postgres")]
77
#[command(about = "Grafbase Postgres Extension")]
8+
#[command(version)]
89
pub struct Args {
910
/// Connection string to the database
1011
#[arg(short, long, env = "DATABASE_URL")]

crates/postgres-introspection/src/config.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use grafbase_database_definition::TableWalker;
22
use serde::Deserialize;
3-
use std::collections::HashMap;
3+
use std::collections::BTreeMap;
44

55
/// Returns the default value for enable_queries configuration.
66
fn default_enable_queries() -> bool {
@@ -33,7 +33,7 @@ pub struct Config {
3333
pub extension_url: String,
3434
/// Configuration details for each schema within the database, keyed by schema name.
3535
#[serde(default)]
36-
pub schemas: HashMap<String, SchemaConfig>,
36+
pub schemas: BTreeMap<String, SchemaConfig>,
3737
}
3838

3939
impl Config {
@@ -105,10 +105,10 @@ pub struct SchemaConfig {
105105
pub enable_queries: Option<bool>,
106106
/// Configuration details for each view within the database, keyed by view name.
107107
#[serde(default)]
108-
pub views: HashMap<String, ViewConfig>,
108+
pub views: BTreeMap<String, ViewConfig>,
109109
/// Configuration overrides for each table within the schema, keyed by table name.
110110
#[serde(default)]
111-
pub tables: HashMap<String, TableConfig>,
111+
pub tables: BTreeMap<String, TableConfig>,
112112
}
113113

114114
#[derive(Deserialize, Debug)]
@@ -120,7 +120,7 @@ pub struct TableConfig {
120120
pub enable_queries: Option<bool>,
121121
/// Configuration details for relationships originating from this view, keyed by relationship name.
122122
#[serde(default)]
123-
pub relations: HashMap<String, RelationConfig>,
123+
pub relations: BTreeMap<String, RelationConfig>,
124124
}
125125

126126
/// Represents the configuration settings for a specific database relation (e.g., a view).
@@ -135,10 +135,10 @@ pub struct ViewConfig {
135135
pub unique_keys: Option<Vec<Vec<String>>>,
136136
/// Configuration details for each column within the relation, keyed by column name.
137137
#[serde(default)]
138-
pub columns: HashMap<String, ColumnConfig>,
138+
pub columns: BTreeMap<String, ColumnConfig>,
139139
/// Configuration details for relationships originating from this view, keyed by relationship name.
140140
#[serde(default)]
141-
pub relations: HashMap<String, RelationConfig>,
141+
pub relations: BTreeMap<String, RelationConfig>,
142142
}
143143

144144
/// Represents the configuration for a specific column within a view.

crates/postgres-introspection/src/foreign_keys.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::HashMap;
1+
use std::collections::BTreeMap;
22

33
use anyhow::bail;
44
use grafbase_database_definition::{DatabaseDefinition, ForeignKey, ForeignKeyColumn, SchemaId};
@@ -160,7 +160,7 @@ fn override_relations(
160160
database_definition: &mut DatabaseDefinition,
161161
constrained_schema_id: SchemaId,
162162
table_name: &str,
163-
relations: &HashMap<String, RelationConfig>,
163+
relations: &BTreeMap<String, RelationConfig>,
164164
) -> anyhow::Result<()> {
165165
let Some(constrained_table_id) = database_definition.get_table_id(constrained_schema_id, table_name) else {
166166
bail!("Table `{table_name}` not found in relation configuration.")

crates/postgres-introspection/src/render/output_types.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,14 +213,14 @@ fn render_page_info(rendered: &mut Schema<'_>) {
213213
});
214214

215215
r#type.push_field({
216-
let mut field = Field::new("startCursor", "String!");
216+
let mut field = Field::new("startCursor", "String");
217217
field.set_description("The cursor of the first item in the page");
218218
field.push_directive(Directive::new("shareable"));
219219
field
220220
});
221221

222222
r#type.push_field({
223-
let mut field = Field::new("endCursor", "String!");
223+
let mut field = Field::new("endCursor", "String");
224224
field.set_description("The cursor of the last item in the page");
225225
field.push_directive(Directive::new("shareable"));
226226
field

crates/sql-ast/src/ast/column.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ impl<'a> Column<'a> {
4646
self.table = Some(table.into());
4747
self
4848
}
49+
50+
/// Returns the name of the column.
51+
pub fn get_name(&self) -> &str {
52+
&self.name
53+
}
4954
}
5055

5156
impl<'a> Aliasable<'a> for Column<'a> {

crates/sql-ast/src/ast/compare.rs

Lines changed: 18 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ pub enum Compare<'a> {
4646
Any(Box<Expression<'a>>),
4747
/// ALL (`left`)
4848
All(Box<Expression<'a>>),
49+
/// `left` IS NOT DISTINCT FROM `right`
50+
IsNotDistinctFrom(Box<Expression<'a>>, Box<Expression<'a>>),
4951
}
5052

5153
#[derive(Debug, Clone, PartialEq)]
@@ -102,6 +104,12 @@ pub trait Comparable<'a> {
102104
where
103105
T: Into<Expression<'a>>;
104106

107+
/// Tests if both sides are not the same value (NULLs are equal).
108+
#[allow(clippy::wrong_self_convention)]
109+
fn is_not_distinct_from<T>(self, comparison: T) -> Compare<'a>
110+
where
111+
T: Into<Expression<'a>>;
112+
105113
/// Tests if the left side is smaller than the right side.
106114
fn less_than<T>(self, comparison: T) -> Compare<'a>
107115
where
@@ -194,26 +202,6 @@ pub trait Comparable<'a> {
194202
where
195203
T: Into<Expression<'a>>;
196204

197-
/// Tests if the JSON array starts with a value.
198-
fn json_array_begins_with<T>(self, item: T) -> Compare<'a>
199-
where
200-
T: Into<Expression<'a>>;
201-
202-
/// Tests if the JSON array does not start with a value.
203-
fn json_array_not_begins_with<T>(self, item: T) -> Compare<'a>
204-
where
205-
T: Into<Expression<'a>>;
206-
207-
/// Tests if the JSON array ends with a value.
208-
fn json_array_ends_into<T>(self, item: T) -> Compare<'a>
209-
where
210-
T: Into<Expression<'a>>;
211-
212-
/// Tests if the JSON array does not end with a value.
213-
fn json_array_not_ends_into<T>(self, item: T) -> Compare<'a>
214-
where
215-
T: Into<Expression<'a>>;
216-
217205
/// Tests if the JSON value is of a certain type.
218206
fn json_type_equals<T>(self, json_type: T) -> Compare<'a>
219207
where
@@ -260,6 +248,16 @@ where
260248
val.not_equals(comparison)
261249
}
262250

251+
fn is_not_distinct_from<T>(self, comparison: T) -> Compare<'a>
252+
where
253+
T: Into<Expression<'a>>,
254+
{
255+
let col: Column<'a> = self.into();
256+
let val: Expression<'a> = col.into();
257+
258+
val.is_not_distinct_from(comparison)
259+
}
260+
263261
fn less_than<T>(self, comparison: T) -> Compare<'a>
264262
where
265263
T: Into<Expression<'a>>,
@@ -418,46 +416,6 @@ where
418416
val.json_array_not_contains(item)
419417
}
420418

421-
fn json_array_begins_with<T>(self, item: T) -> Compare<'a>
422-
where
423-
T: Into<Expression<'a>>,
424-
{
425-
let col: Column<'a> = self.into();
426-
let val: Expression<'a> = col.into();
427-
428-
val.json_array_begins_with(item)
429-
}
430-
431-
fn json_array_not_begins_with<T>(self, item: T) -> Compare<'a>
432-
where
433-
T: Into<Expression<'a>>,
434-
{
435-
let col: Column<'a> = self.into();
436-
let val: Expression<'a> = col.into();
437-
438-
val.json_array_not_begins_with(item)
439-
}
440-
441-
fn json_array_ends_into<T>(self, item: T) -> Compare<'a>
442-
where
443-
T: Into<Expression<'a>>,
444-
{
445-
let col: Column<'a> = self.into();
446-
let val: Expression<'a> = col.into();
447-
448-
val.json_array_ends_into(item)
449-
}
450-
451-
fn json_array_not_ends_into<T>(self, item: T) -> Compare<'a>
452-
where
453-
T: Into<Expression<'a>>,
454-
{
455-
let col: Column<'a> = self.into();
456-
let val: Expression<'a> = col.into();
457-
458-
val.json_array_not_ends_into(item)
459-
}
460-
461419
fn json_type_equals<T>(self, json_type: T) -> Compare<'a>
462420
where
463421
T: Into<JsonType<'a>>,

crates/sql-ast/src/ast/expression.rs

Lines changed: 7 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,13 @@ impl<'a> Comparable<'a> for Expression<'a> {
217217
Compare::NotEquals(Box::new(self), Box::new(comparison.into()))
218218
}
219219

220+
fn is_not_distinct_from<T>(self, comparison: T) -> Compare<'a>
221+
where
222+
T: Into<Expression<'a>>,
223+
{
224+
Compare::IsNotDistinctFrom(Box::new(self), Box::new(comparison.into()))
225+
}
226+
220227
fn less_than<T>(self, comparison: T) -> Compare<'a>
221228
where
222229
T: Into<Expression<'a>>,
@@ -349,42 +356,6 @@ impl<'a> Comparable<'a> for Expression<'a> {
349356
Compare::Json(JsonCompare::ArrayNotContains(Box::new(self), Box::new(item.into())))
350357
}
351358

352-
fn json_array_begins_with<T>(self, item: T) -> Compare<'a>
353-
where
354-
T: Into<Expression<'a>>,
355-
{
356-
let array_starts_with: Expression = super::function::json_extract_first_array_elem(self).into();
357-
358-
Compare::Equals(Box::new(array_starts_with), Box::new(item.into()))
359-
}
360-
361-
fn json_array_not_begins_with<T>(self, item: T) -> Compare<'a>
362-
where
363-
T: Into<Expression<'a>>,
364-
{
365-
let array_starts_with: Expression = super::function::json_extract_first_array_elem(self).into();
366-
367-
Compare::NotEquals(Box::new(array_starts_with), Box::new(item.into()))
368-
}
369-
370-
fn json_array_ends_into<T>(self, item: T) -> Compare<'a>
371-
where
372-
T: Into<Expression<'a>>,
373-
{
374-
let array_ends_into: Expression = super::function::json_extract_last_array_elem(self).into();
375-
376-
Compare::Equals(Box::new(array_ends_into), Box::new(item.into()))
377-
}
378-
379-
fn json_array_not_ends_into<T>(self, item: T) -> Compare<'a>
380-
where
381-
T: Into<Expression<'a>>,
382-
{
383-
let array_ends_into: Expression = super::function::json_extract_last_array_elem(self).into();
384-
385-
Compare::NotEquals(Box::new(array_ends_into), Box::new(item.into()))
386-
}
387-
388359
fn json_type_equals<T>(self, json_type: T) -> Compare<'a>
389360
where
390361
T: Into<JsonType<'a>>,

crates/sql-ast/src/ast/function.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ mod average;
44
mod cast;
55
mod coalesce;
66
mod concat;
7+
mod convert_from;
78
mod count;
9+
mod decode;
810
mod encode;
911
mod json_agg;
12+
mod json_build_array;
1013
mod json_build_object;
1114
mod json_extract;
1215
mod json_extract_array;
@@ -27,12 +30,15 @@ pub use average::*;
2730
pub use cast::*;
2831
pub use coalesce::*;
2932
pub use concat::*;
33+
pub use convert_from::*;
3034
pub use count::*;
35+
pub use decode::*;
3136
pub use encode::*;
3237
pub use json_agg::*;
38+
pub use json_build_array::*;
3339
pub use json_build_object::*;
3440
pub use json_extract::*;
35-
pub(crate) use json_extract_array::*;
41+
pub use json_extract_array::*;
3642
pub use json_unquote::*;
3743
pub use lower::*;
3844
pub use maximum::*;
@@ -49,18 +55,17 @@ use super::{Alias, Aliasable};
4955
/// A database function definition
5056
#[derive(Debug, Clone, PartialEq)]
5157
pub struct Function<'a> {
52-
pub(crate) typ_: FunctionType<'a>,
58+
pub(crate) r#type: FunctionType<'a>,
5359
pub(crate) alias: Option<Alias<'a>>,
5460
}
5561

5662
impl Function<'_> {
5763
pub fn returns_json(&self) -> bool {
5864
matches!(
59-
self.typ_,
65+
self.r#type,
6066
FunctionType::RowToJson(_)
6167
| FunctionType::JsonExtract(_)
62-
| FunctionType::JsonExtractLastArrayElem(_)
63-
| FunctionType::JsonExtractFirstArrayElem(_)
68+
| FunctionType::JsonExtractArrayElem(_)
6469
| FunctionType::ToJsonb(_)
6570
)
6671
}
@@ -81,8 +86,7 @@ pub(crate) enum FunctionType<'a> {
8186
Coalesce(Coalesce<'a>),
8287
Concat(Concat<'a>),
8388
JsonExtract(JsonExtract<'a>),
84-
JsonExtractLastArrayElem(JsonExtractLastArrayElem<'a>),
85-
JsonExtractFirstArrayElem(JsonExtractFirstArrayElem<'a>),
89+
JsonExtractArrayElem(JsonExtractArrayElem<'a>),
8690
JsonUnquote(JsonUnquote<'a>),
8791
RowToJson(RowToJson<'a>),
8892
ToJsonb(ToJsonb<'a>),
@@ -91,6 +95,10 @@ pub(crate) enum FunctionType<'a> {
9195
JsonBuildObject(JsonBuildObject<'a>),
9296
Unnest(Unnest<'a>),
9397
ArrayPosition(ArrayPosition<'a>),
98+
JsonBuildArray(JsonBuildArray<'a>),
99+
RowNumber(RowNumber<'a>),
100+
Decode(Decode<'a>),
101+
ConvertFrom(ConvertFrom<'a>),
94102
}
95103

96104
impl<'a> Aliasable<'a> for Function<'a> {

crates/sql-ast/src/ast/function/aggregate_to_string.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ where
2323
impl<'a> From<AggregateToString<'a>> for Function<'a> {
2424
fn from(value: AggregateToString<'a>) -> Self {
2525
Self {
26-
typ_: FunctionType::AggregateToString(value),
26+
r#type: FunctionType::AggregateToString(value),
2727
alias: None,
2828
}
2929
}

crates/sql-ast/src/ast/function/array_position.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ where
2424
impl<'a> From<ArrayPosition<'a>> for Function<'a> {
2525
fn from(value: ArrayPosition<'a>) -> Self {
2626
Self {
27-
typ_: FunctionType::ArrayPosition(value),
27+
r#type: FunctionType::ArrayPosition(value),
2828
alias: None,
2929
}
3030
}

crates/sql-ast/src/ast/function/average.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ where
1919
impl<'a> From<Average<'a>> for Function<'a> {
2020
fn from(value: Average<'a>) -> Self {
2121
Self {
22-
typ_: FunctionType::Average(value),
22+
r#type: FunctionType::Average(value),
2323
alias: None,
2424
}
2525
}

0 commit comments

Comments
 (0)