|
27 | 27 | #include"commands/dbcommands.h"
|
28 | 28 | #include"funcapi.h"
|
29 | 29 | #include"miscadmin.h"
|
| 30 | +#include"parser/scansup.h" |
30 | 31 | #include"parser/keywords.h"
|
31 | 32 | #include"postmaster/syslogger.h"
|
32 | 33 | #include"rewrite/rewriteHandler.h"
|
@@ -719,3 +720,226 @@ pg_column_is_updatable(PG_FUNCTION_ARGS)
|
719 | 720 |
|
720 | 721 | PG_RETURN_BOOL((events&REQ_EVENTS)==REQ_EVENTS);
|
721 | 722 | }
|
| 723 | + |
| 724 | + |
| 725 | +/* |
| 726 | + * This simple parser utility are compatible with lexer implementation, |
| 727 | + * used only in parse_ident function |
| 728 | + */ |
| 729 | +staticbool |
| 730 | +is_ident_start(unsignedcharc) |
| 731 | +{ |
| 732 | +if (c=='_') |
| 733 | +return true; |
| 734 | +if ((c >='a'&&c <='z')|| (c >='A'&&c <='Z')) |
| 735 | +return true; |
| 736 | + |
| 737 | +if (c >=0200&&c <=0377) |
| 738 | +return true; |
| 739 | + |
| 740 | +return false; |
| 741 | +} |
| 742 | + |
| 743 | +staticbool |
| 744 | +is_ident_cont(unsignedcharc) |
| 745 | +{ |
| 746 | +if (c >='0'&&c <='9') |
| 747 | +return true; |
| 748 | + |
| 749 | +returnis_ident_start(c); |
| 750 | +} |
| 751 | + |
| 752 | +/* |
| 753 | + * Sanitize SQL string for using in error message. |
| 754 | + */ |
| 755 | +staticchar* |
| 756 | +sanitize_text(text*t) |
| 757 | +{ |
| 758 | +intlen=VARSIZE_ANY_EXHDR(t); |
| 759 | +constchar*p=VARDATA_ANY(t); |
| 760 | +StringInfodstr; |
| 761 | + |
| 762 | +dstr=makeStringInfo(); |
| 763 | + |
| 764 | +appendStringInfoChar(dstr,'"'); |
| 765 | + |
| 766 | +while (len--) |
| 767 | +{ |
| 768 | +switch (*p) |
| 769 | +{ |
| 770 | +case'\b': |
| 771 | +appendStringInfoString(dstr,"\\b"); |
| 772 | +break; |
| 773 | +case'\f': |
| 774 | +appendStringInfoString(dstr,"\\f"); |
| 775 | +break; |
| 776 | +case'\n': |
| 777 | +appendStringInfoString(dstr,"\\n"); |
| 778 | +break; |
| 779 | +case'\r': |
| 780 | +appendStringInfoString(dstr,"\\r"); |
| 781 | +break; |
| 782 | +case'\t': |
| 783 | +appendStringInfoString(dstr,"\\t"); |
| 784 | +break; |
| 785 | +case'\'': |
| 786 | +appendStringInfoString(dstr,"''"); |
| 787 | +break; |
| 788 | +case'\\': |
| 789 | +appendStringInfoString(dstr,"\\\\"); |
| 790 | +break; |
| 791 | +default: |
| 792 | +if ((unsignedchar)*p<' ') |
| 793 | +appendStringInfo(dstr,"\\u%04x", (int)*p); |
| 794 | +else |
| 795 | +appendStringInfoCharMacro(dstr,*p); |
| 796 | +break; |
| 797 | +} |
| 798 | +p++; |
| 799 | +} |
| 800 | + |
| 801 | +appendStringInfoChar(dstr,'"'); |
| 802 | + |
| 803 | +returndstr->data; |
| 804 | +} |
| 805 | + |
| 806 | +/* |
| 807 | + * parse_ident - parse SQL composed identifier to separate identifiers. |
| 808 | + * When strict mode is active (second parameter), then any chars after |
| 809 | + * last identifiers are disallowed. |
| 810 | + */ |
| 811 | +Datum |
| 812 | +parse_ident(PG_FUNCTION_ARGS) |
| 813 | +{ |
| 814 | +text*qualname; |
| 815 | +char*qualname_str; |
| 816 | +boolstrict; |
| 817 | +char*nextp; |
| 818 | +boolafter_dot= false; |
| 819 | +ArrayBuildState*astate=NULL; |
| 820 | + |
| 821 | +qualname=PG_GETARG_TEXT_PP(0); |
| 822 | +qualname_str=text_to_cstring(qualname); |
| 823 | +strict=PG_GETARG_BOOL(1); |
| 824 | + |
| 825 | +nextp=qualname_str; |
| 826 | + |
| 827 | +/* skip leading whitespace */ |
| 828 | +while (isspace((unsignedchar)*nextp)) |
| 829 | +nextp++; |
| 830 | + |
| 831 | +for (;;) |
| 832 | +{ |
| 833 | +char*curname; |
| 834 | +char*endp; |
| 835 | +boolmissing_ident; |
| 836 | + |
| 837 | +missing_ident= true; |
| 838 | + |
| 839 | +if (*nextp=='\"') |
| 840 | +{ |
| 841 | +curname=nextp+1; |
| 842 | +for (;;) |
| 843 | +{ |
| 844 | +endp=strchr(nextp+1,'\"'); |
| 845 | +if (endp==NULL) |
| 846 | +ereport(ERROR, |
| 847 | +(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 848 | +errmsg("unclosed double quotes"), |
| 849 | +errdetail("string %s is not valid identifier", |
| 850 | +sanitize_text(qualname)))); |
| 851 | +if (endp[1]!='\"') |
| 852 | +break; |
| 853 | +memmove(endp,endp+1,strlen(endp)); |
| 854 | +nextp=endp; |
| 855 | +} |
| 856 | +nextp=endp+1; |
| 857 | +*endp='\0'; |
| 858 | + |
| 859 | +/* Show complete input string in this case. */ |
| 860 | +if (endp-curname==0) |
| 861 | +ereport(ERROR, |
| 862 | +(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 863 | +errmsg("identifier should not be empty: %s", |
| 864 | +sanitize_text(qualname)))); |
| 865 | + |
| 866 | +astate=accumArrayResult(astate,CStringGetTextDatum(curname), |
| 867 | + false,TEXTOID,CurrentMemoryContext); |
| 868 | +missing_ident= false; |
| 869 | +} |
| 870 | +else |
| 871 | +{ |
| 872 | +if (is_ident_start((unsignedchar)*nextp)) |
| 873 | +{ |
| 874 | +char*downname; |
| 875 | +intlen; |
| 876 | +text*part; |
| 877 | + |
| 878 | +curname=nextp++; |
| 879 | +while (is_ident_cont((unsignedchar)*nextp)) |
| 880 | +nextp++; |
| 881 | + |
| 882 | +len=nextp-curname; |
| 883 | + |
| 884 | +/* |
| 885 | + * Unlike name, we don't implicitly truncate identifiers. This |
| 886 | + * is useful for allowing the user to check for specific parts |
| 887 | + * of the identifier being too long. It's easy enough for the |
| 888 | + * user to get the truncated names by casting our output to |
| 889 | + * name[]. |
| 890 | + */ |
| 891 | +downname=downcase_identifier(curname,len, false, false); |
| 892 | +part=cstring_to_text_with_len(downname,len); |
| 893 | +astate=accumArrayResult(astate,PointerGetDatum(part), false, |
| 894 | +TEXTOID,CurrentMemoryContext); |
| 895 | +missing_ident= false; |
| 896 | +} |
| 897 | +} |
| 898 | + |
| 899 | +if (missing_ident) |
| 900 | +{ |
| 901 | +/* Different error messages based on where we failed. */ |
| 902 | +if (*nextp=='.') |
| 903 | +ereport(ERROR, |
| 904 | +(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 905 | +errmsg("missing valid identifier before \".\" symbol: %s", |
| 906 | +sanitize_text(qualname)))); |
| 907 | +elseif (after_dot) |
| 908 | +ereport(ERROR, |
| 909 | +(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 910 | +errmsg("missing valid identifier after \".\" symbol: %s", |
| 911 | +sanitize_text(qualname)))); |
| 912 | +else |
| 913 | +ereport(ERROR, |
| 914 | +(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 915 | +errmsg("missing valid identifier: %s", |
| 916 | +sanitize_text(qualname)))); |
| 917 | +} |
| 918 | + |
| 919 | +while (isspace((unsignedchar)*nextp)) |
| 920 | +nextp++; |
| 921 | + |
| 922 | +if (*nextp=='.') |
| 923 | +{ |
| 924 | +after_dot= true; |
| 925 | +nextp++; |
| 926 | +while (isspace((unsignedchar)*nextp)) |
| 927 | +nextp++; |
| 928 | +} |
| 929 | +elseif (*nextp=='\0') |
| 930 | +{ |
| 931 | +break; |
| 932 | +} |
| 933 | +else |
| 934 | +{ |
| 935 | +if (strict) |
| 936 | +ereport(ERROR, |
| 937 | +(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 938 | +errmsg("identifier contains disallowed characters: %s", |
| 939 | +sanitize_text(qualname)))); |
| 940 | +break; |
| 941 | +} |
| 942 | +} |
| 943 | + |
| 944 | +PG_RETURN_DATUM(makeArrayResult(astate,CurrentMemoryContext)); |
| 945 | +} |