feat: add cursor statements (#5094)

* feat: add sql parsers for cursor operations

* feat: cursor operator

* feat: implement RecordBatchStreamCursor

* feat: implement cursor storage and execution

* test: add tests

* chore: update docstring

* feat: add a temporary sql rewrite for cast in limit

this issue is described in #5097

* test: add more sql for cursor integration test

* feat: reject non-select query for cursor statement

* refactor: address review issues

* test: add empty result case

* feat: address review comments
This commit is contained in:
Ning Sun
2024-12-06 17:32:22 +08:00
committed by GitHub
parent 8b944268da
commit 3133f3fb4e
21 changed files with 786 additions and 5 deletions

View File

@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::borrow::Cow;
use std::collections::HashMap;
use std::sync::Arc;
@@ -112,6 +113,13 @@ pub(crate) fn process<'a>(query: &str, query_ctx: QueryContextRef) -> Option<Vec
}
}
static LIMIT_CAST_PATTERN: Lazy<Regex> =
Lazy::new(|| Regex::new("(?i)(LIMIT\\s+\\d+)::bigint").unwrap());
pub(crate) fn rewrite_sql(query: &str) -> Cow<'_, str> {
//TODO(sunng87): remove this when we upgraded datafusion to 43 or newer
LIMIT_CAST_PATTERN.replace_all(query, "$1")
}
#[cfg(test)]
mod test {
use session::context::{QueryContext, QueryContextRef};
@@ -195,4 +203,13 @@ mod test {
assert!(process("SHOW TABLES ", query_context.clone()).is_none());
assert!(process("SET TIME_ZONE=utc ", query_context.clone()).is_none());
}
#[test]
fn test_rewrite() {
let sql = "SELECT * FROM number LIMIT 1::bigint";
let sql2 = "SELECT * FROM number limit 1::BIGINT";
assert_eq!("SELECT * FROM number LIMIT 1", rewrite_sql(sql));
assert_eq!("SELECT * FROM number limit 1", rewrite_sql(sql2));
}
}

View File

@@ -70,6 +70,9 @@ impl SimpleQueryHandler for PostgresServerHandlerInner {
return Ok(vec![Response::EmptyQuery]);
}
let query = fixtures::rewrite_sql(query);
let query = query.as_ref();
if let Some(resps) = fixtures::process(query, query_ctx.clone()) {
send_warning_opt(client, query_ctx).await?;
Ok(resps)
@@ -229,6 +232,9 @@ impl QueryParser for DefaultQueryParser {
});
}
let sql = fixtures::rewrite_sql(sql);
let sql = sql.as_ref();
let mut stmts =
ParserContext::create_with_dialect(sql, &PostgreSqlDialect {}, ParseOptions::default())
.map_err(|e| PgWireError::ApiError(Box::new(e)))?;