Skip to main content

pgrx_sql_entity_graph/pg_extern/entity/
mod.rs

1//LICENSE Portions Copyright 2019-2021 ZomboDB, LLC.
2//LICENSE
3//LICENSE Portions Copyright 2021-2023 Technology Concepts & Design, Inc.
4//LICENSE
5//LICENSE Portions Copyright 2023-2023 PgCentral Foundation, Inc. <contact@pgcentral.org>
6//LICENSE
7//LICENSE All rights reserved.
8//LICENSE
9//LICENSE Use of this source code is governed by the MIT license that can be found in the LICENSE file.
10/*!
11
12`#[pg_extern]` related entities for Rust to SQL translation
13
14> Like all of the [`sql_entity_graph`][crate] APIs, this is considered **internal**
15> to the `pgrx` framework and very subject to change between versions. While you may use this, please do it with caution.
16
17*/
18mod argument;
19mod cast;
20mod operator;
21mod returning;
22
23pub use argument::PgExternArgumentEntity;
24pub use cast::PgCastEntity;
25pub use operator::PgOperatorEntity;
26pub use returning::{PgExternReturnEntity, PgExternReturnEntityIteratedItem};
27
28use crate::fmt;
29use crate::metadata::{Returns, SqlMapping};
30use crate::pgrx_sql::PgrxSql;
31use crate::to_sql::ToSql;
32use crate::to_sql::entity::ToSqlConfigEntity;
33use crate::{ExternArgs, SqlGraphEntity, SqlGraphIdentifier, TypeMatch};
34
35use eyre::{WrapErr, eyre};
36
37/// The output of a [`PgExtern`](crate::pg_extern::PgExtern) from `quote::ToTokens::to_tokens`.
38#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
39pub struct PgExternEntity {
40    pub name: &'static str,
41    pub unaliased_name: &'static str,
42    pub module_path: &'static str,
43    pub full_path: &'static str,
44    pub metadata: crate::metadata::FunctionMetadataEntity,
45    pub fn_args: Vec<PgExternArgumentEntity>,
46    pub fn_return: PgExternReturnEntity,
47    pub schema: Option<&'static str>,
48    pub file: &'static str,
49    pub line: u32,
50    pub extern_attrs: Vec<ExternArgs>,
51    pub search_path: Option<Vec<&'static str>>,
52    pub operator: Option<PgOperatorEntity>,
53    pub cast: Option<PgCastEntity>,
54    pub to_sql_config: ToSqlConfigEntity,
55}
56
57impl From<PgExternEntity> for SqlGraphEntity {
58    fn from(val: PgExternEntity) -> Self {
59        SqlGraphEntity::Function(val)
60    }
61}
62
63impl SqlGraphIdentifier for PgExternEntity {
64    fn dot_identifier(&self) -> String {
65        format!("fn {}", self.name)
66    }
67    fn rust_identifier(&self) -> String {
68        self.full_path.to_string()
69    }
70
71    fn file(&self) -> Option<&'static str> {
72        Some(self.file)
73    }
74
75    fn line(&self) -> Option<u32> {
76        Some(self.line)
77    }
78}
79
80impl PgExternEntity {
81    fn sql_name(&self, context: &PgrxSql) -> String {
82        let self_index = context.externs[self];
83        let schema = self
84            .schema
85            .map(|schema| format!("{schema}."))
86            .unwrap_or_else(|| context.schema_prefix_for(&self_index));
87
88        format!("{schema}\"{}\"", self.name)
89    }
90}
91
92impl ToSql for PgExternEntity {
93    fn to_sql(&self, context: &PgrxSql) -> eyre::Result<String> {
94        let self_index = context.externs[self];
95        let mut extern_attrs = self.extern_attrs.clone();
96        // if we already have a STRICT marker we do not need to add it
97        // presume we can upgrade, then disprove it
98        let mut strict_upgrade = !extern_attrs.iter().any(|i| i == &ExternArgs::Strict);
99        if strict_upgrade {
100            // It may be possible to infer a `STRICT` marker though.
101            // But we can only do that if the user hasn't used `Option<T>` or `pgrx::Internal`
102            for arg in &self.metadata.arguments {
103                if arg.optional {
104                    strict_upgrade = false;
105                }
106            }
107        }
108
109        if strict_upgrade {
110            extern_attrs.push(ExternArgs::Strict);
111        }
112        extern_attrs.sort();
113        extern_attrs.dedup();
114
115        let module_pathname = &context.get_module_pathname();
116        let schema = self
117            .schema
118            .map(|schema| format!("{schema}."))
119            .unwrap_or_else(|| context.schema_prefix_for(&self_index));
120        let arguments = if !self.fn_args.is_empty() {
121            let mut args = Vec::new();
122            let metadata_without_arg_skips = &self
123                .metadata
124                .arguments
125                .iter()
126                .filter(|v| v.argument_sql != Ok(SqlMapping::Skip))
127                .collect::<Vec<_>>();
128            for (idx, arg) in self.fn_args.iter().enumerate() {
129                let graph_index = context
130                    .graph
131                    .neighbors_undirected(self_index)
132                    .find(|neighbor| context.graph[*neighbor].type_matches(&arg.used_ty))
133                    .ok_or_else(|| eyre!("Could not find arg type in graph. Got: {:?}", arg))?;
134                let needs_comma = idx < (metadata_without_arg_skips.len().saturating_sub(1));
135                let metadata_argument = &self.metadata.arguments[idx];
136                match metadata_argument.argument_sql {
137                    Ok(SqlMapping::As(ref argument_sql)) => {
138                        let buf = format!(
139                            "\
140                                            \t\"{pattern}\" {variadic}{schema_prefix}{sql_type}{default}{maybe_comma}/* {type_name} */\
141                                        ",
142                            pattern = arg.pattern,
143                            schema_prefix = context.schema_prefix_for(&graph_index),
144                            // First try to match on [`TypeId`] since it's most reliable.
145                            sql_type = argument_sql,
146                            default = if let Some(def) = arg.used_ty.default {
147                                format!(" DEFAULT {def}")
148                            } else {
149                                String::from("")
150                            },
151                            variadic = if metadata_argument.variadic { "VARIADIC " } else { "" },
152                            maybe_comma = if needs_comma { ", " } else { " " },
153                            type_name = metadata_argument.type_name,
154                        );
155                        args.push(buf);
156                    }
157                    Ok(SqlMapping::Composite { array_brackets }) => {
158                        let sql = self.fn_args[idx]
159                            .used_ty
160                            .composite_type
161                            .map(|v| fmt::with_array_brackets(v.into(), array_brackets))
162                            .ok_or_else(|| {
163                                eyre!(
164                                    "Macro expansion time suggested a composite_type!() in return"
165                                )
166                            })?;
167                        let buf = format!(
168                            "\
169                            \t\"{pattern}\" {variadic}{schema_prefix}{sql_type}{default}{maybe_comma}/* {type_name} */\
170                        ",
171                            pattern = arg.pattern,
172                            schema_prefix = context.schema_prefix_for(&graph_index),
173                            // First try to match on [`TypeId`] since it's most reliable.
174                            sql_type = sql,
175                            default = if let Some(def) = arg.used_ty.default {
176                                format!(" DEFAULT {def}")
177                            } else {
178                                String::from("")
179                            },
180                            variadic = if metadata_argument.variadic { "VARIADIC " } else { "" },
181                            maybe_comma = if needs_comma { ", " } else { " " },
182                            type_name = metadata_argument.type_name,
183                        );
184                        args.push(buf);
185                    }
186                    Ok(SqlMapping::Skip) => (),
187                    Err(err) => return Err(err).wrap_err("While mapping argument"),
188                }
189            }
190            String::from("\n") + &args.join("\n") + "\n"
191        } else {
192            Default::default()
193        };
194
195        let returns = match &self.fn_return {
196            PgExternReturnEntity::None => String::from("RETURNS void"),
197            PgExternReturnEntity::Type { ty } => {
198                let graph_index = context
199                    .graph
200                    .neighbors_undirected(self_index)
201                    .find(|neighbor| context.graph[*neighbor].type_matches(ty))
202                    .ok_or_else(|| eyre!("Could not find return type in graph."))?;
203                let metadata_retval = self.metadata.retval.clone();
204                let sql_type = match metadata_retval.return_sql {
205                    Ok(Returns::One(SqlMapping::As(ref sql))) => sql.clone(),
206                    Ok(Returns::One(SqlMapping::Composite { array_brackets })) => {
207                        fmt::with_array_brackets(ty.composite_type.unwrap().into(), array_brackets)
208                    }
209                    Ok(other) => {
210                        return Err(eyre!(
211                            "Got non-plain mapped/composite return variant SQL in what macro-expansion thought was a type, got: {other:?}"
212                        ));
213                    }
214                    Err(err) => return Err(err).wrap_err("Error mapping return SQL"),
215                };
216                format!(
217                    "RETURNS {schema_prefix}{sql_type} /* {full_path} */",
218                    schema_prefix = context.schema_prefix_for(&graph_index),
219                    full_path = ty.full_path
220                )
221            }
222            PgExternReturnEntity::SetOf { ty, .. } => {
223                let graph_index = context
224                    .graph
225                    .neighbors_undirected(self_index)
226                    .find(|neighbor| context.graph[*neighbor].type_matches(ty))
227                    .ok_or_else(|| eyre!("Could not find return type in graph."))?;
228                let metadata_retval = self.metadata.retval.clone();
229                let sql_type = match metadata_retval.return_sql {
230                    Ok(Returns::SetOf(SqlMapping::As(ref sql))) => sql.clone(),
231                    Ok(Returns::SetOf(SqlMapping::Composite { array_brackets })) => {
232                        fmt::with_array_brackets(ty.composite_type.unwrap().into(), array_brackets)
233                    }
234                    Ok(_other) => {
235                        return Err(eyre!(
236                            "Got non-setof mapped/composite return variant SQL in what macro-expansion thought was a setof"
237                        ));
238                    }
239                    Err(err) => return Err(err).wrap_err("Error mapping return SQL"),
240                };
241                format!(
242                    "RETURNS SETOF {schema_prefix}{sql_type} /* {full_path} */",
243                    schema_prefix = context.schema_prefix_for(&graph_index),
244                    full_path = ty.full_path
245                )
246            }
247            PgExternReturnEntity::Iterated { tys: table_items, .. } => {
248                let mut items = String::new();
249                let metadata_retval = self.metadata.retval.clone();
250                let metadata_retval_sqls: Vec<String> = match metadata_retval.return_sql {
251                    Ok(Returns::Table(variants)) => variants
252                        .iter()
253                        .enumerate()
254                        .map(|(idx, variant)| match variant {
255                            SqlMapping::As(sql) => sql.clone(),
256                            SqlMapping::Composite { array_brackets } => {
257                                let composite = table_items[idx].ty.composite_type.unwrap();
258                                fmt::with_array_brackets(composite.into(), *array_brackets)
259                            }
260                            SqlMapping::Skip => todo!(),
261                        })
262                        .collect(),
263                    Ok(_other) => {
264                        return Err(eyre!(
265                            "Got non-table return variant SQL in what macro-expansion thought was a table"
266                        ));
267                    }
268                    Err(err) => return Err(err).wrap_err("Error mapping return SQL"),
269                };
270
271                for (idx, returning::PgExternReturnEntityIteratedItem { ty, name: col_name }) in
272                    table_items.iter().enumerate()
273                {
274                    let graph_index =
275                        context.graph.neighbors_undirected(self_index).find(|neighbor| {
276                            context.graph[*neighbor].id_or_name_matches(&ty.ty_id, ty.ty_source)
277                        });
278
279                    let needs_comma = idx < (table_items.len() - 1);
280                    let item = format!(
281                        "\n\t{col_name} {schema_prefix}{ty_resolved}{needs_comma} /* {ty_name} */",
282                        col_name = col_name.expect(
283                            "An iterator of tuples should have `named!()` macro declarations."
284                        ),
285                        schema_prefix = if let Some(graph_index) = graph_index {
286                            context.schema_prefix_for(&graph_index)
287                        } else {
288                            "".into()
289                        },
290                        ty_resolved = metadata_retval_sqls[idx],
291                        needs_comma = if needs_comma { ", " } else { " " },
292                        ty_name = ty.full_path
293                    );
294                    items.push_str(&item);
295                }
296                format!("RETURNS TABLE ({items}\n)")
297            }
298            PgExternReturnEntity::Trigger => String::from("RETURNS trigger"),
299        };
300        let PgExternEntity { name, module_path, file, line, .. } = self;
301
302        let fn_sql = format!(
303            "\
304                CREATE {or_replace} FUNCTION {schema}\"{name}\"({arguments}) {returns}\n\
305                {extern_attrs}\
306                {search_path}\
307                LANGUAGE c /* Rust */\n\
308                AS '{module_pathname}', '{unaliased_name}_wrapper';\
309            ",
310            or_replace =
311                if extern_attrs.contains(&ExternArgs::CreateOrReplace) { "OR REPLACE" } else { "" },
312            search_path = if let Some(search_path) = &self.search_path {
313                let retval = format!("SET search_path TO {}", search_path.join(", "));
314                retval + "\n"
315            } else {
316                Default::default()
317            },
318            extern_attrs = if extern_attrs.is_empty() {
319                String::default()
320            } else {
321                let mut retval = extern_attrs
322                    .iter()
323                    .filter(|attr| **attr != ExternArgs::CreateOrReplace)
324                    .map(|attr| {
325                        if matches!(attr, ExternArgs::Support(..)) {
326                            let support_fn_name = attr.to_string();
327
328                            let support_fn_name =
329                            if let Some(entity) = context.find_matching_fn(&support_fn_name) {
330                                entity.sql_name(context)
331                            } else {
332                                panic!("cannot locate SUPPORT function `{support_fn_name}` attached to function `{}`", self.full_path)
333                            };
334
335                            format!("SUPPORT {support_fn_name}")
336                        } else {
337                            attr.to_string().to_uppercase()
338                        }
339                    })
340                    .collect::<Vec<_>>()
341                    .join(" ");
342                retval.push('\n');
343                retval
344            },
345            unaliased_name = self.unaliased_name,
346        );
347
348        let requires = {
349            let requires_attrs = self
350                .extern_attrs
351                .iter()
352                .filter_map(|x| match x {
353                    ExternArgs::Requires(requirements) => Some(requirements.clone()),
354                    ExternArgs::Support(support_fn) => Some(vec![support_fn.clone()]),
355                    _ => None,
356                })
357                .flatten()
358                .collect::<Vec<_>>();
359
360            if !requires_attrs.is_empty() {
361                format!(
362                    "-- requires:\n{}\n",
363                    requires_attrs
364                        .iter()
365                        .map(|i| format!("--   {i}"))
366                        .collect::<Vec<_>>()
367                        .join("\n")
368                )
369            } else {
370                "".to_string()
371            }
372        };
373
374        let mut ext_sql = format!(
375            "\n\
376            -- {file}:{line}\n\
377            -- {module_path}::{name}\n\
378            {requires}\
379            {fn_sql}"
380        );
381
382        if let Some(op) = &self.operator {
383            let mut optionals = vec![];
384            if let Some(it) = op.commutator {
385                optionals.push(format!("\tCOMMUTATOR = {it}"));
386            };
387            if let Some(it) = op.negator {
388                optionals.push(format!("\tNEGATOR = {it}"));
389            };
390            if let Some(it) = op.restrict {
391                optionals.push(format!("\tRESTRICT = {it}"));
392            };
393            if let Some(it) = op.join {
394                optionals.push(format!("\tJOIN = {it}"));
395            };
396            if op.hashes {
397                optionals.push(String::from("\tHASHES"));
398            };
399            if op.merges {
400                optionals.push(String::from("\tMERGES"));
401            };
402
403            let left_arg =
404                self.metadata.arguments.first().ok_or_else(|| {
405                    eyre!("Did not find `left_arg` for operator `{}`.", self.name)
406                })?;
407            let left_fn_arg = self
408                .fn_args
409                .first()
410                .ok_or_else(|| eyre!("Did not find `left_arg` for operator `{}`.", self.name))?;
411            let left_arg_graph_index = context
412                .graph
413                .neighbors_undirected(self_index)
414                .find(|neighbor| {
415                    context.graph[*neighbor]
416                        .id_or_name_matches(&left_fn_arg.used_ty.ty_id, left_arg.type_name)
417                })
418                .ok_or_else(|| {
419                    eyre!("Could not find left arg type in graph. Got: {:?}", left_arg)
420                })?;
421            let left_arg_sql = match left_arg.argument_sql {
422                Ok(SqlMapping::As(ref sql)) => sql.clone(),
423                Ok(SqlMapping::Composite { array_brackets }) => {
424                    if array_brackets {
425                        let composite_type = self.fn_args[0].used_ty.composite_type
426                            .ok_or(eyre!("Found a composite type but macro expansion time did not reveal a name, use `pgrx::composite_type!()`"))?;
427                        format!("{composite_type}[]")
428                    } else {
429                        self.fn_args[0].used_ty.composite_type
430                            .ok_or(eyre!("Found a composite type but macro expansion time did not reveal a name, use `pgrx::composite_type!()`"))?.to_string()
431                    }
432                }
433                Ok(SqlMapping::Skip) => {
434                    return Err(eyre!(
435                        "Found an skipped SQL type in an operator, this is not valid"
436                    ));
437                }
438                Err(err) => return Err(err.into()),
439            };
440
441            let right_arg =
442                self.metadata.arguments.get(1).ok_or_else(|| {
443                    eyre!("Did not find `left_arg` for operator `{}`.", self.name)
444                })?;
445            let right_fn_arg = self
446                .fn_args
447                .get(1)
448                .ok_or_else(|| eyre!("Did not find `left_arg` for operator `{}`.", self.name))?;
449            let right_arg_graph_index = context
450                .graph
451                .neighbors_undirected(self_index)
452                .find(|neighbor| {
453                    context.graph[*neighbor]
454                        .id_or_name_matches(&right_fn_arg.used_ty.ty_id, right_arg.type_name)
455                })
456                .ok_or_else(|| {
457                    eyre!("Could not find right arg type in graph. Got: {:?}", right_arg)
458                })?;
459            let right_arg_sql = match right_arg.argument_sql {
460                Ok(SqlMapping::As(ref sql)) => sql.clone(),
461                Ok(SqlMapping::Composite { array_brackets }) => {
462                    if array_brackets {
463                        let composite_type = self.fn_args[1].used_ty.composite_type
464                            .ok_or(eyre!("Found a composite type but macro expansion time did not reveal a name, use `pgrx::composite_type!()`"))?;
465                        format!("{composite_type}[]")
466                    } else {
467                        self.fn_args[0].used_ty.composite_type
468                            .ok_or(eyre!("Found a composite type but macro expansion time did not reveal a name, use `pgrx::composite_type!()`"))?.to_string()
469                    }
470                }
471                Ok(SqlMapping::Skip) => {
472                    return Err(eyre!(
473                        "Found an skipped SQL type in an operator, this is not valid"
474                    ));
475                }
476                Err(err) => return Err(err.into()),
477            };
478
479            let schema = self
480                .schema
481                .map(|schema| format!("{schema}."))
482                .unwrap_or_else(|| context.schema_prefix_for(&self_index));
483
484            let operator_sql = format!(
485                "\n\n\
486                                                    -- {file}:{line}\n\
487                                                    -- {module_path}::{name}\n\
488                                                    CREATE OPERATOR {schema}{opname} (\n\
489                                                        \tPROCEDURE={schema}\"{name}\",\n\
490                                                        \tLEFTARG={schema_prefix_left}{left_arg_sql}, /* {left_name} */\n\
491                                                        \tRIGHTARG={schema_prefix_right}{right_arg_sql}{maybe_comma} /* {right_name} */\n\
492                                                        {optionals}\
493                                                    );\
494                                                    ",
495                opname = op.opname.unwrap(),
496                left_name = left_arg.type_name,
497                right_name = right_arg.type_name,
498                schema_prefix_left = context.schema_prefix_for(&left_arg_graph_index),
499                schema_prefix_right = context.schema_prefix_for(&right_arg_graph_index),
500                maybe_comma = if !optionals.is_empty() { "," } else { "" },
501                optionals = if !optionals.is_empty() {
502                    optionals.join(",\n") + "\n"
503                } else {
504                    "".to_string()
505                },
506            );
507            ext_sql += &operator_sql
508        };
509        if let Some(cast) = &self.cast {
510            let target_arg = &self.metadata.retval;
511            let target_fn_arg = &self.fn_return;
512            let target_arg_graph_index = context
513                .graph
514                .neighbors_undirected(self_index)
515                .find(|neighbor| match (&context.graph[*neighbor], target_fn_arg) {
516                    (SqlGraphEntity::Type(ty), PgExternReturnEntity::Type { ty: rty }) => {
517                        ty.id_matches(&rty.ty_id)
518                    }
519                    (SqlGraphEntity::Enum(en), PgExternReturnEntity::Type { ty: rty }) => {
520                        en.id_matches(&rty.ty_id)
521                    }
522                    (SqlGraphEntity::BuiltinType(defined), _) => defined == target_arg.type_name,
523                    _ => false,
524                })
525                .ok_or_else(|| {
526                    eyre!("Could not find source type in graph. Got: {:?}", target_arg)
527                })?;
528            let target_arg_sql = match target_arg.argument_sql {
529                Ok(SqlMapping::As(ref sql)) => sql.clone(),
530                Ok(SqlMapping::Composite { array_brackets }) => {
531                    if array_brackets {
532                        let composite_type = self.fn_args[0].used_ty.composite_type
533                            .ok_or(eyre!("Found a composite type but macro expansion time did not reveal a name, use `pgrx::composite_type!()`"))?;
534                        format!("{composite_type}[]")
535                    } else {
536                        self.fn_args[0].used_ty.composite_type
537                            .ok_or(eyre!("Found a composite type but macro expansion time did not reveal a name, use `pgrx::composite_type!()`"))?.to_string()
538                    }
539                }
540                Ok(SqlMapping::Skip) => {
541                    return Err(eyre!("Found an skipped SQL type in a cast, this is not valid"));
542                }
543                Err(err) => return Err(err.into()),
544            };
545            let source_arg = self
546                .metadata
547                .arguments
548                .first()
549                .ok_or_else(|| eyre!("Did not find source type for cast `{}`.", self.name))?;
550            let source_fn_arg = self
551                .fn_args
552                .first()
553                .ok_or_else(|| eyre!("Did not find source type for cast `{}`.", self.name))?;
554            let source_arg_graph_index = context
555                .graph
556                .neighbors_undirected(self_index)
557                .find(|neighbor| {
558                    context.graph[*neighbor]
559                        .id_or_name_matches(&source_fn_arg.used_ty.ty_id, source_arg.type_name)
560                })
561                .ok_or_else(|| {
562                    eyre!("Could not find source type in graph. Got: {:?}", source_arg)
563                })?;
564            let source_arg_sql = match source_arg.argument_sql {
565                Ok(SqlMapping::As(ref sql)) => sql.clone(),
566                Ok(SqlMapping::Composite { array_brackets }) => {
567                    if array_brackets {
568                        let composite_type = self.fn_args[0].used_ty.composite_type
569                            .ok_or(eyre!("Found a composite type but macro expansion time did not reveal a name, use `pgrx::composite_type!()`"))?;
570                        format!("{composite_type}[]")
571                    } else {
572                        self.fn_args[0].used_ty.composite_type
573                            .ok_or(eyre!("Found a composite type but macro expansion time did not reveal a name, use `pgrx::composite_type!()`"))?.to_string()
574                    }
575                }
576                Ok(SqlMapping::Skip) => {
577                    return Err(eyre!("Found an skipped SQL type in a cast, this is not valid"));
578                }
579                Err(err) => return Err(err.into()),
580            };
581            let optional = match cast {
582                PgCastEntity::Default => String::from(""),
583                PgCastEntity::Assignment => String::from(" AS ASSIGNMENT"),
584                PgCastEntity::Implicit => String::from(" AS IMPLICIT"),
585            };
586
587            let cast_sql = format!(
588                "\n\n\
589                                                    -- {file}:{line}\n\
590                                                    -- {module_path}::{name}\n\
591                                                    CREATE CAST (\n\
592                                                        \t{schema_prefix_source}{source_arg_sql} /* {source_name} */\n\
593                                                        \tAS\n\
594                                                        \t{schema_prefix_target}{target_arg_sql} /* {target_name} */\n\
595                                                    )\n\
596                                                    WITH FUNCTION {function_name}{optional};\
597                                                    ",
598                file = self.file,
599                line = self.line,
600                name = self.name,
601                module_path = self.module_path,
602                schema_prefix_source = context.schema_prefix_for(&source_arg_graph_index),
603                source_name = source_arg.type_name,
604                schema_prefix_target = context.schema_prefix_for(&target_arg_graph_index),
605                target_name = target_arg.type_name,
606                function_name = self.name,
607            );
608            ext_sql += &cast_sql
609        };
610        Ok(ext_sql)
611    }
612}