1use std::fmt::Debug;
16use std::pin::Pin;
17use std::sync::Arc;
18
19use async_trait::async_trait;
20use common_query::{Output, OutputData};
21use common_recordbatch::RecordBatch;
22use common_recordbatch::error::Result as RecordBatchResult;
23use common_telemetry::{debug, info, tracing};
24use datafusion::sql::sqlparser::ast::{CopyOption, CopyTarget, Statement as SqlParserStatement};
25use datafusion_common::ParamValues;
26use datafusion_expr::LogicalPlan;
27use datafusion_pg_catalog::sql::PostgresCompatibilityParser;
28use datatypes::prelude::ConcreteDataType;
29use datatypes::schema::{Schema, SchemaRef};
30use futures::{Sink, SinkExt, Stream, StreamExt, future, stream};
31use operator::statement::admin_output_schema;
32use pgwire::api::portal::{Format, Portal};
33use pgwire::api::query::{ExtendedQueryHandler, SimpleQueryHandler};
34use pgwire::api::results::{
35 CopyCsvOptions, CopyEncoder, CopyResponse, CopyTextOptions, DataRowEncoder,
36 DescribePortalResponse, DescribeStatementResponse, FieldInfo, QueryResponse, Response, Tag,
37};
38use pgwire::api::stmt::{QueryParser, StoredStatement};
39use pgwire::api::{ClientInfo, ErrorHandler, Type};
40use pgwire::error::{ErrorInfo, PgWireError, PgWireResult};
41use pgwire::messages::PgWireBackendMessage;
42use pgwire::messages::copy::CopyData;
43use pgwire::messages::data::DataRow;
44use query::dist_analyze_output_schema;
45use query::planner::DfLogicalPlanner;
46use query::query_engine::DescribeResult;
47use query::sql::DESCRIBE_TABLE_OUTPUT_SCHEMA;
48use session::Session;
49use session::context::QueryContextRef;
50use snafu::ResultExt;
51use sql::dialect::PostgreSqlDialect;
52use sql::parser::{ParseOptions, ParserContext};
53use sql::statements::statement::Statement;
54
55use crate::SqlPlan;
56use crate::error::{DataFusionSnafu, InferParameterTypesSnafu, Result};
57use crate::postgres::types::*;
58use crate::postgres::utils::convert_err;
59use crate::postgres::{PostgresServerHandlerInner, copy_in, fixtures};
60use crate::query_handler::sql::ServerSqlQueryHandlerRef;
61
62impl PostgresServerHandlerInner {
63 pub(super) fn new_query_context(&self) -> QueryContextRef {
67 let mut ctx = self.session.new_query_context();
68 Arc::make_mut(&mut ctx).set_batching_enabled(self.batching_enabled);
69 ctx
70 }
71}
72
73#[async_trait]
74impl SimpleQueryHandler for PostgresServerHandlerInner {
75 #[tracing::instrument(skip_all, fields(protocol = "postgres"))]
76 async fn do_query<C>(&self, client: &mut C, query: &str) -> PgWireResult<Vec<Response>>
77 where
78 C: ClientInfo + Sink<PgWireBackendMessage> + Unpin + Send + Sync,
79 C::Error: Debug,
80 PgWireError: From<<C as Sink<PgWireBackendMessage>>::Error>,
81 {
82 let query_ctx = self.new_query_context();
83 let db = query_ctx.get_db_string();
84 let _timer = crate::metrics::METRIC_POSTGRES_QUERY_TIMER
85 .with_label_values(&[crate::metrics::METRIC_POSTGRES_SIMPLE_QUERY, db.as_str()])
86 .start_timer();
87
88 if query.is_empty() {
89 return Ok(vec![Response::EmptyQuery]);
91 }
92
93 if let Some(copy_from_stdin) = copy_in::copy_from_stdin_from_sql(query)? {
97 let response = self.begin_copy_in(copy_from_stdin).await?;
98 send_warning_opt(client, query_ctx).await?;
99 return Ok(vec![response]);
100 }
101
102 let parsed_query = self.query_parser.compatibility_parser.parse(query);
103
104 if let Ok(statements) = &parsed_query
107 && statements.iter().any(copy_in::is_copy_from_stdin)
108 {
109 return Err(copy_in::copy_in_must_run_alone_error());
110 }
111
112 let query = if let Ok(statements) = &parsed_query {
113 if statements.is_empty() {
115 return Ok(vec![Response::EmptyQuery]);
116 }
117 statements
118 .iter()
119 .map(statement_to_query)
120 .collect::<Vec<_>>()
121 .join(";")
122 } else {
123 query.to_string()
124 };
125
126 if let Some(resps) = fixtures::process(&query, query_ctx.clone()) {
127 send_warning_opt(client, query_ctx).await?;
128 Ok(resps)
129 } else {
130 let outputs = self.query_handler.do_query(&query, query_ctx.clone()).await;
131
132 let mut results = Vec::with_capacity(outputs.len());
133
134 let statements = parsed_query.ok();
135 for (idx, output) in outputs.into_iter().enumerate() {
136 let copy_format = statements
137 .as_ref()
138 .and_then(|stmts| stmts.get(idx))
139 .and_then(check_copy_to_stdout);
140 let resp = if let Some(format) = ©_format {
141 output_to_copy_response(query_ctx.clone(), output, format)?
142 } else {
143 output_to_query_response(query_ctx.clone(), output, &Format::UnifiedText)?
144 };
145 results.push(resp);
146 }
147
148 send_warning_opt(client, query_ctx).await?;
149 Ok(results)
150 }
151 }
152}
153
154fn statement_to_query(statement: &SqlParserStatement) -> String {
155 if let SqlParserStatement::ShowVariable { variable } = statement
156 && variable.len() > 1
157 && (variable[0].value.eq_ignore_ascii_case("query")
158 || variable[0].value.eq_ignore_ascii_case("datafusion"))
159 {
160 return format!(
161 "SHOW {}",
162 variable
163 .iter()
164 .map(ToString::to_string)
165 .collect::<Vec<_>>()
166 .join(".")
167 );
168 }
169 statement.to_string()
170}
171
172async fn send_warning_opt<C>(client: &mut C, query_context: QueryContextRef) -> PgWireResult<()>
173where
174 C: Sink<PgWireBackendMessage> + Unpin + Send + Sync,
175 C::Error: Debug,
176 PgWireError: From<<C as Sink<PgWireBackendMessage>>::Error>,
177{
178 if let Some(warning) = query_context.warning() {
179 client
180 .feed(PgWireBackendMessage::NoticeResponse(
181 ErrorInfo::new(
182 PgErrorSeverity::Warning.to_string(),
183 PgErrorCode::Ec01000.code(),
184 warning.clone(),
185 )
186 .into(),
187 ))
188 .await?;
189 }
190
191 Ok(())
192}
193
194pub(crate) fn output_to_query_response(
195 query_ctx: QueryContextRef,
196 output: Result<Output>,
197 field_format: &Format,
198) -> PgWireResult<Response> {
199 match output {
200 Ok(o) => match o.data {
201 OutputData::AffectedRows(rows) => {
202 Ok(Response::Execution(Tag::new("OK").with_rows(rows)))
203 }
204 OutputData::Stream(record_stream) => {
205 let schema = record_stream.schema();
206 recordbatches_to_query_response(query_ctx, record_stream, schema, field_format)
207 }
208 OutputData::RecordBatches(recordbatches) => {
209 let schema = recordbatches.schema();
210 recordbatches_to_query_response(
211 query_ctx,
212 recordbatches.as_stream(),
213 schema,
214 field_format,
215 )
216 }
217 },
218 Err(e) => Err(convert_err(e)),
219 }
220}
221
222type RowStream<T> = Pin<Box<dyn Stream<Item = PgWireResult<T>> + Send + Unpin>>;
223
224fn recordbatches_to_query_response<S>(
225 query_ctx: QueryContextRef,
226 recordbatches_stream: S,
227 schema: SchemaRef,
228 field_format: &Format,
229) -> PgWireResult<Response>
230where
231 S: Stream<Item = RecordBatchResult<RecordBatch>> + Send + Unpin + 'static,
232{
233 let format_options = format_options_from_query_ctx(&query_ctx);
234 let pg_schema = Arc::new(
235 schema_to_pg(schema.as_ref(), field_format, Some(format_options)).map_err(convert_err)?,
236 );
237
238 let encoder = DataRowEncoder::new(pg_schema.clone());
239 let row_stream = RecordBatchRowStream::new(
240 query_ctx.clone(),
241 pg_schema.clone(),
242 schema.clone(),
243 recordbatches_stream,
244 encoder,
245 );
246
247 let data_row_stream: RowStream<DataRow> = Box::pin(
248 row_stream
249 .map(move |result| match result {
250 Ok(rows) => Box::pin(stream::iter(rows.into_iter().map(Ok))) as RowStream<DataRow>,
251 Err(e) => Box::pin(stream::once(future::ready(Err(e)))) as RowStream<DataRow>,
252 })
253 .flatten(),
254 );
255
256 Ok(Response::Query(QueryResponse::new(
257 pg_schema,
258 data_row_stream,
259 )))
260}
261
262pub(crate) fn output_to_copy_response(
263 query_ctx: QueryContextRef,
264 output: Result<Output>,
265 format: &str,
266) -> PgWireResult<Response> {
267 match output {
268 Ok(o) => match o.data {
269 OutputData::AffectedRows(_) => Err(PgWireError::UserError(Box::new(ErrorInfo::new(
270 "ERROR".to_string(),
271 "42601".to_string(),
272 "COPY cannot be used with non-query statements".to_string(),
273 )))),
274 OutputData::Stream(record_stream) => {
275 let schema = record_stream.schema();
276 recordbatches_to_copy_response(query_ctx, record_stream, schema, format)
277 }
278 OutputData::RecordBatches(recordbatches) => {
279 let schema = recordbatches.schema();
280 recordbatches_to_copy_response(query_ctx, recordbatches.as_stream(), schema, format)
281 }
282 },
283 Err(e) => Err(convert_err(e)),
284 }
285}
286
287fn recordbatches_to_copy_response<S>(
288 query_ctx: QueryContextRef,
289 recordbatches_stream: S,
290 schema: SchemaRef,
291 format: &str,
292) -> PgWireResult<Response>
293where
294 S: Stream<Item = RecordBatchResult<RecordBatch>> + Send + Unpin + 'static,
295{
296 let format_options = format_options_from_query_ctx(&query_ctx);
297 let pg_fields = schema_to_pg(schema.as_ref(), &Format::UnifiedText, Some(format_options))
298 .map_err(convert_err)?;
299
300 let copy_format = match format.to_lowercase().as_str() {
301 "binary" => 1,
302 _ => 0,
303 };
304
305 let arrow_schema_override = copy_out_schema_with_timezone(&schema, &query_ctx);
313 let pg_fields = if arrow_schema_override.is_some() {
314 pg_fields
315 .into_iter()
316 .map(|mut field| {
317 if field.datatype() == &Type::TIMESTAMP {
318 field = FieldInfo::new(
319 field.name().to_string(),
320 None,
321 None,
322 Type::TIMESTAMPTZ,
323 field.format(),
324 )
325 .with_type_size(field.type_size())
326 .with_type_modifier(field.type_modifier());
327 }
328 field
329 })
330 .collect::<Vec<_>>()
331 } else {
332 pg_fields
333 };
334
335 let pg_schema = Arc::new(pg_fields);
336 let num_columns = pg_schema.len();
337
338 let copy_encoder = match format.to_lowercase().as_str() {
339 "csv" => CopyEncoder::new_csv(pg_schema.clone(), CopyCsvOptions::default()),
340 "binary" => CopyEncoder::new_binary(pg_schema.clone()),
341 _ => CopyEncoder::new_text(pg_schema.clone(), CopyTextOptions::default()),
342 };
343
344 let row_stream = RecordBatchRowStream::new(
345 query_ctx.clone(),
346 pg_schema.clone(),
347 schema.clone(),
348 recordbatches_stream,
349 copy_encoder,
350 )
351 .with_arrow_schema_override(arrow_schema_override);
352
353 let copy_stream: RowStream<CopyData> = Box::pin(
354 row_stream
355 .map(move |result| match result {
356 Ok(rows) => Box::pin(stream::iter(rows.into_iter().map(Ok))) as RowStream<CopyData>,
357 Err(e) => Box::pin(stream::once(future::ready(Err(e)))) as RowStream<CopyData>,
358 })
359 .flatten(),
360 );
361
362 Ok(Response::CopyOut(CopyResponse::new(
363 copy_format,
364 num_columns,
365 copy_stream,
366 )))
367}
368
369pub struct DefaultQueryParser {
370 query_handler: ServerSqlQueryHandlerRef,
371 session: Arc<Session>,
372 compatibility_parser: PostgresCompatibilityParser,
373}
374
375impl DefaultQueryParser {
376 pub fn new(query_handler: ServerSqlQueryHandlerRef, session: Arc<Session>) -> Self {
377 DefaultQueryParser {
378 query_handler,
379 session,
380 compatibility_parser: PostgresCompatibilityParser::new(),
381 }
382 }
383}
384
385#[derive(Clone, Debug)]
387pub struct PgSqlPlan {
388 pub(crate) plan: SqlPlan,
389 pub(crate) copy_to_stdout_format: Option<String>,
390 pub(crate) copy_from_stdin: Option<copy_in::CopyFromStdin>,
391}
392
393#[async_trait]
394impl QueryParser for DefaultQueryParser {
395 type Statement = PgSqlPlan;
396
397 async fn parse_sql<C>(
398 &self,
399 _client: &C,
400 sql: &str,
401 _types: &[Option<Type>],
402 ) -> PgWireResult<Option<Self::Statement>> {
403 crate::metrics::METRIC_POSTGRES_PREPARED_COUNT.inc();
404 let query_ctx = self.session.new_query_context();
405
406 if sql.is_empty() {
408 return Ok(None);
409 }
410
411 if fixtures::matches(sql) {
412 return Ok(Some(PgSqlPlan {
413 plan: SqlPlan::Shortcut(sql.to_string()),
414 copy_to_stdout_format: None,
415 copy_from_stdin: None,
416 }));
417 }
418
419 if let Some(copy_from_stdin) = copy_in::copy_from_stdin_from_sql(sql)? {
424 return Ok(Some(PgSqlPlan {
425 plan: SqlPlan::Shortcut(sql.to_string()),
426 copy_to_stdout_format: None,
427 copy_from_stdin: Some(copy_from_stdin),
428 }));
429 }
430
431 let parsed_statements = self.compatibility_parser.parse(sql);
432 let (sql, copy_to_stdout_format) = if let Ok(mut statements) = parsed_statements {
433 if statements.is_empty() {
434 return Ok(None);
435 }
436 let first_stmt = statements.remove(0);
437 let format = check_copy_to_stdout(&first_stmt);
438 (first_stmt.to_string(), format)
439 } else {
440 (sql.to_string(), None)
443 };
444
445 let mut stmts = ParserContext::create_with_dialect(
446 &sql,
447 &PostgreSqlDialect {},
448 ParseOptions::default(),
449 )
450 .map_err(convert_err)?;
451 if stmts.len() != 1 {
452 Err(PgWireError::UserError(Box::new(ErrorInfo::from(
453 PgErrorCode::Ec42P14,
454 ))))
455 } else {
456 let stmt = stmts.remove(0);
457
458 if let Some(logical_plan) = self
459 .query_handler
460 .do_describe(stmt.clone(), query_ctx)
461 .await
462 .map_err(convert_err)?
463 .map(|DescribeResult { logical_plan }| logical_plan)
464 {
465 Ok(Some(PgSqlPlan {
466 plan: SqlPlan::Plan(logical_plan, stmt),
467 copy_to_stdout_format,
468 copy_from_stdin: None,
469 }))
470 } else {
471 Ok(Some(PgSqlPlan {
472 plan: SqlPlan::Statement(stmt, sql),
473 copy_to_stdout_format,
474 copy_from_stdin: None,
475 }))
476 }
477 }
478 }
479
480 fn get_parameter_types(&self, _stmt: &Self::Statement) -> PgWireResult<Vec<Type>> {
481 Err(PgWireError::ApiError(
484 "get_parameter_types is not expected to be called".into(),
485 ))
486 }
487
488 fn get_result_schema(
489 &self,
490 _stmt: &Self::Statement,
491 _column_format: Option<&Format>,
492 ) -> PgWireResult<Vec<FieldInfo>> {
493 Err(PgWireError::ApiError(
496 "get_result_schema is not expected to be called".into(),
497 ))
498 }
499}
500
501#[async_trait]
502impl ExtendedQueryHandler for PostgresServerHandlerInner {
503 type Statement = PgSqlPlan;
504 type QueryParser = DefaultQueryParser;
505
506 fn query_parser(&self) -> Arc<Self::QueryParser> {
507 self.query_parser.clone()
508 }
509
510 async fn do_query<C>(
511 &self,
512 client: &mut C,
513 portal: &Portal<Self::Statement>,
514 _max_rows: usize,
515 ) -> PgWireResult<Response>
516 where
517 C: ClientInfo + Sink<PgWireBackendMessage> + Unpin + Send + Sync,
518 C::Error: Debug,
519 PgWireError: From<<C as Sink<PgWireBackendMessage>>::Error>,
520 {
521 let query_ctx = self.new_query_context();
522 let db = query_ctx.get_db_string();
523 let _timer = crate::metrics::METRIC_POSTGRES_QUERY_TIMER
524 .with_label_values(&[crate::metrics::METRIC_POSTGRES_EXTENDED_QUERY, db.as_str()])
525 .start_timer();
526
527 let pg_sql_plan = &portal.statement.statement;
528 let sql_plan = &pg_sql_plan.plan;
529
530 if let Some(copy_from_stdin) = &pg_sql_plan.copy_from_stdin {
533 let response = self.begin_copy_in(copy_from_stdin.clone()).await?;
534 send_warning_opt(client, query_ctx).await?;
535 return Ok(response);
536 }
537
538 let output = match sql_plan {
539 SqlPlan::Empty => {
540 return Ok(Response::EmptyQuery);
542 }
543 SqlPlan::Shortcut(query) => {
544 if let Some(mut resps) = fixtures::process(query, query_ctx.clone()) {
545 send_warning_opt(client, query_ctx).await?;
546 return Ok(resps.remove(0));
548 } else {
549 return Ok(Response::EmptyQuery);
551 }
552 }
553 SqlPlan::Plan(plan, stmt) => {
554 let values = parameters_to_scalar_values(plan, portal)?;
555 let plan = plan
556 .clone()
557 .replace_params_with_values(&ParamValues::List(
558 values.into_iter().map(Into::into).collect(),
559 ))
560 .context(DataFusionSnafu)
561 .map_err(convert_err)?;
562 self.query_handler
563 .do_exec_plan(plan, Some(stmt.clone()), query_ctx.clone())
564 .await
565 }
566 SqlPlan::Statement(_stmt, query) => {
567 self.query_handler
573 .do_query(query, query_ctx.clone())
574 .await
575 .remove(0)
576 }
577 };
578
579 send_warning_opt(client, query_ctx.clone()).await?;
580
581 if let Some(format) = &pg_sql_plan.copy_to_stdout_format {
582 output_to_copy_response(query_ctx, output, format)
583 } else {
584 output_to_query_response(query_ctx, output, &portal.result_column_format)
585 }
586 }
587
588 async fn do_describe_statement<C>(
589 &self,
590 _client: &mut C,
591 stmt: &StoredStatement<Self::Statement>,
592 ) -> PgWireResult<DescribeStatementResponse>
593 where
594 C: ClientInfo + Unpin + Send + Sync,
595 {
596 let sql_plan = &stmt.statement.plan;
597 let provided_param_types = &stmt.parameter_types;
599 let server_inferenced_types = if let SqlPlan::Plan(plan, _) = &sql_plan {
600 let param_types = DfLogicalPlanner::get_inferred_parameter_types(plan)
601 .context(InferParameterTypesSnafu)
602 .map_err(convert_err)?
603 .into_iter()
604 .map(|(k, v)| (k, v.map(|v| ConcreteDataType::from_arrow_type(&v))))
605 .collect();
606
607 let types = param_types_to_pg_types(¶m_types).map_err(convert_err)?;
608
609 Some(types)
610 } else {
611 None
612 };
613
614 let param_count = if provided_param_types.is_empty() {
615 server_inferenced_types
616 .as_ref()
617 .map(|types| types.len())
618 .unwrap_or(0)
619 } else {
620 provided_param_types.len()
621 };
622
623 let param_types = (0..param_count)
624 .map(|i| {
625 let client_type = provided_param_types.get(i);
626 match client_type {
628 Some(Some(client_type)) => client_type.clone(),
629 _ => server_inferenced_types
630 .as_ref()
631 .and_then(|types| types.get(i).cloned())
632 .unwrap_or(Type::UNKNOWN),
633 }
634 })
635 .collect::<Vec<_>>();
636
637 let fields = describe_fields(sql_plan, &Format::UnifiedText, &self.session)?;
638
639 Ok(DescribeStatementResponse::new(param_types, fields))
640 }
641
642 async fn do_describe_portal<C>(
643 &self,
644 _client: &mut C,
645 portal: &Portal<Self::Statement>,
646 ) -> PgWireResult<DescribePortalResponse>
647 where
648 C: ClientInfo + Unpin + Send + Sync,
649 {
650 let sql_plan = &portal.statement.statement.plan;
651 let format = &portal.result_column_format;
652
653 let fields = describe_fields(sql_plan, format, &self.session)?;
654
655 Ok(DescribePortalResponse::new(fields))
656 }
657}
658
659fn describe_fields(
660 sql_plan: &SqlPlan,
661 format: &Format,
662 session: &Arc<Session>,
663) -> PgWireResult<Vec<FieldInfo>> {
664 match sql_plan {
665 SqlPlan::Plan(LogicalPlan::Analyze(_), _) => {
668 let schema: Schema =
669 Schema::try_from(dist_analyze_output_schema()).map_err(convert_err)?;
670 schema_to_pg(&schema, format, None).map_err(convert_err)
671 }
672 SqlPlan::Plan(plan, _) if !matches!(plan, LogicalPlan::Dml(_) | LogicalPlan::Ddl(_)) => {
674 let schema: Schema = plan.schema().clone().try_into().map_err(convert_err)?;
675 schema_to_pg(&schema, format, None).map_err(convert_err)
676 }
677 SqlPlan::Statement(
680 Statement::ShowCreateDatabase(_)
681 | Statement::ShowCreateTable(_)
682 | Statement::ShowCreateFlow(_)
683 | Statement::ShowCreateView(_),
684 _,
685 ) => Ok(vec![
686 FieldInfo::new(
687 "name".to_string(),
688 None,
689 None,
690 Type::TEXT,
691 format.format_for(0),
692 ),
693 FieldInfo::new(
694 "create_statement".to_string(),
695 None,
696 None,
697 Type::TEXT,
698 format.format_for(1),
699 ),
700 ]),
701 #[cfg(feature = "enterprise")]
702 SqlPlan::Statement(Statement::ShowCreateTrigger(_), _) => Ok(vec![
703 FieldInfo::new(
704 "name".to_string(),
705 None,
706 None,
707 Type::TEXT,
708 format.format_for(0),
709 ),
710 FieldInfo::new(
711 "create_statement".to_string(),
712 None,
713 None,
714 Type::TEXT,
715 format.format_for(1),
716 ),
717 ]),
718 SqlPlan::Statement(Statement::ShowFlowStatus(_), _) => Ok(vec![
721 FieldInfo::new(
722 "flow_id".to_string(),
723 None,
724 None,
725 Type::INT8, format.format_for(0),
727 ),
728 FieldInfo::new(
729 "flow_name".to_string(),
730 None,
731 None,
732 Type::TEXT,
733 format.format_for(1),
734 ),
735 FieldInfo::new(
736 "start_time".to_string(),
737 None,
738 None,
739 Type::TIMESTAMP,
740 format.format_for(2),
741 ),
742 FieldInfo::new(
743 "last_execution_time".to_string(),
744 None,
745 None,
746 Type::TIMESTAMP,
747 format.format_for(3),
748 ),
749 FieldInfo::new(
750 "uptime_seconds".to_string(),
751 None,
752 None,
753 Type::INT8,
754 format.format_for(4),
755 ),
756 FieldInfo::new(
757 "state_size".to_string(),
758 None,
759 None,
760 Type::NUMERIC,
761 format.format_for(5),
762 ),
763 ]),
764
765 #[cfg(feature = "enterprise")]
766 SqlPlan::Statement(Statement::ShowTriggers(_), _) => Ok(vec![FieldInfo::new(
767 "name".to_string(),
768 None,
769 None,
770 Type::TEXT,
771 format.format_for(0),
772 )]),
773 SqlPlan::Shortcut(query) => {
776 if let Some(mut resp) = fixtures::process(query, session.new_query_context())
778 && let Response::Query(query_response) = resp.remove(0)
779 {
780 Ok((*query_response.row_schema()).clone())
781 } else {
782 Ok(vec![])
784 }
785 }
786 SqlPlan::Statement(Statement::ShowVariables(show), _) => Ok(vec![FieldInfo::new(
788 show.variable.to_string().to_uppercase(),
789 None,
790 None,
791 Type::TEXT,
792 format.format_for(0),
793 )]),
794 SqlPlan::Statement(Statement::ShowStatus(_), _) => Ok(vec![
796 FieldInfo::new(
797 "Variable_name".to_string(),
798 None,
799 None,
800 Type::TEXT,
801 format.format_for(0),
802 ),
803 FieldInfo::new(
804 "Value".to_string(),
805 None,
806 None,
807 Type::TEXT,
808 format.format_for(1),
809 ),
810 ]),
811 SqlPlan::Statement(Statement::ShowSearchPath(_), _) => Ok(vec![FieldInfo::new(
812 "search_path".to_string(),
813 None,
814 None,
815 Type::TEXT,
816 format.format_for(0),
817 )]),
818 SqlPlan::Statement(Statement::DescribeTable(_), _) => {
820 schema_to_pg(&DESCRIBE_TABLE_OUTPUT_SCHEMA, format, None).map_err(convert_err)
821 }
822 SqlPlan::Statement(Statement::Admin(admin), _) => {
825 let query_ctx = session.new_query_context();
826 match admin_output_schema(admin, &query_ctx) {
827 Some(schema) => schema_to_pg(&schema, format, None).map_err(convert_err),
828 None => Ok(vec![]),
830 }
831 }
832 SqlPlan::Statement(Statement::FetchCursor(fetch), _) => {
834 let cursor_name = fetch.cursor_name.to_string();
835 match session.get_cursor(&cursor_name) {
836 Some(cursor) => schema_to_pg(&cursor.schema(), format, None).map_err(convert_err),
837 None => Ok(vec![]),
839 }
840 }
841 _ => {
842 Ok(vec![])
844 }
845 }
846}
847
848impl ErrorHandler for PostgresServerHandlerInner {
849 fn on_error<C>(&self, _client: &C, error: &mut PgWireError)
850 where
851 C: ClientInfo,
852 {
853 match error {
854 PgWireError::IoError(e) => debug!("Postgres client disconnected: {}", e),
855 _ => info!("Postgres interface error: {}", error),
856 }
857 }
858}
859
860fn copy_out_schema_with_timezone(
866 schema: &SchemaRef,
867 query_ctx: &QueryContextRef,
868) -> Option<Arc<arrow::datatypes::Schema>> {
869 let timezone = query_ctx.timezone().to_string();
870 let arrow_schema = schema.arrow_schema();
871 let mut changed = false;
872 let fields = arrow_schema
873 .fields()
874 .iter()
875 .map(|field| match field.data_type() {
876 arrow::datatypes::DataType::Timestamp(unit, None) => {
877 changed = true;
878 field
879 .as_ref()
880 .clone()
881 .with_data_type(arrow::datatypes::DataType::Timestamp(
882 *unit,
883 Some(Arc::from(timezone.as_str())),
884 ))
885 }
886 _ => field.as_ref().clone(),
887 })
888 .collect::<Vec<_>>();
889 changed.then(|| Arc::new(arrow::datatypes::Schema::new(fields)))
890}
891
892fn check_copy_to_stdout(statement: &SqlParserStatement) -> Option<String> {
893 if let SqlParserStatement::Copy {
894 target, options, ..
895 } = statement
896 && matches!(target, CopyTarget::Stdout)
897 {
898 for opt in options {
899 if let CopyOption::Format(format_ident) = opt {
900 return Some(format_ident.value.to_lowercase());
901 }
902 }
903 return Some("txt".to_string());
904 }
905
906 None
907}
908
909#[cfg(test)]
910mod tests {
911 use datafusion_pg_catalog::sql::PostgresCompatibilityParser;
912
913 use super::*;
914
915 fn parse_copy_statement(sql: &str) -> SqlParserStatement {
916 let parser = PostgresCompatibilityParser::new();
917 let statements = parser.parse(sql).unwrap();
918 statements.into_iter().next().unwrap()
919 }
920
921 #[test]
922 fn test_check_copy_out_with_csv_format() {
923 let statement = parse_copy_statement("COPY (SELECT 1) TO STDOUT WITH (FORMAT CSV)");
924 assert_eq!(check_copy_to_stdout(&statement), Some("csv".to_string()));
925 }
926
927 #[test]
928 fn test_check_copy_out_with_txt_format() {
929 let statement = parse_copy_statement("COPY (SELECT 1) TO STDOUT WITH (FORMAT TXT)");
930 assert_eq!(check_copy_to_stdout(&statement), Some("txt".to_string()));
931 }
932
933 #[test]
934 fn test_check_copy_out_with_binary_format() {
935 let statement = parse_copy_statement("COPY (SELECT 1) TO STDOUT WITH (FORMAT BINARY)");
936 assert_eq!(check_copy_to_stdout(&statement), Some("binary".to_string()));
937 }
938
939 #[test]
940 fn test_check_copy_out_without_format() {
941 let statement = parse_copy_statement("COPY (SELECT 1) TO STDOUT");
942 assert_eq!(check_copy_to_stdout(&statement), Some("txt".to_string()));
943 }
944
945 #[test]
946 fn test_check_copy_out_to_file() {
947 let statement =
948 parse_copy_statement("COPY (SELECT 1) TO '/path/to/file.csv' WITH (FORMAT CSV)");
949 assert_eq!(check_copy_to_stdout(&statement), None);
950 }
951
952 #[test]
953 fn test_check_copy_out_case_insensitive() {
954 let statement = parse_copy_statement("COPY (SELECT 1) TO STDOUT WITH (FORMAT csv)");
955 assert_eq!(check_copy_to_stdout(&statement), Some("csv".to_string()));
956
957 let statement = parse_copy_statement("COPY (SELECT 1) TO STDOUT WITH (FORMAT binary)");
958 assert_eq!(check_copy_to_stdout(&statement), Some("binary".to_string()));
959 }
960
961 #[test]
962 fn test_check_copy_out_with_multiple_options() {
963 let statement = parse_copy_statement(
964 "COPY (SELECT 1) TO STDOUT WITH (FORMAT csv, DELIMITER ',', HEADER)",
965 );
966 assert_eq!(check_copy_to_stdout(&statement), Some("csv".to_string()));
967
968 let statement = parse_copy_statement(
969 "COPY (SELECT 1) TO STDOUT WITH (DELIMITER ',', HEADER, FORMAT binary)",
970 );
971 assert_eq!(check_copy_to_stdout(&statement), Some("binary".to_string()));
972 }
973}