/*------------------------------------------------------------------------- * * psql_sub.c * A sub set of psql(an interactive front-end) * * Copyright (c) 1996, Regents of the University of California * Copyright 2000 by PostgreSQL Global Development Group * * This software was modified from "psql.c" by SAKAIDA Masaaki -- * Osaka,Japan. * * * Change logs * 2000.03.22: Change PQmblen(s) to PSmblen(s,encoding) * Add include "mb.h" * Change PSprint(fout,opt) ==> PSprint(fout,opt,encoding) * 2000.03.23: Change (dF)FunctionsList query for PostgreSQL ver 6.x.x * and Ver 7.x.x. * Change pset->db to pset->dbset->db. * 2000.04.21: Change that SQLCA is not set when END is issued. * 2000.04.28: Change list All DBs to new query. * 2001.04.06: Delete "mb.h". Delete PSmblen(). * 2001.04.15: Delete SendCommand() and backslash_routines. * 2001.04.15: Add large_object routines. * 2001.06.11: Add
tag into a SQL parse error message. * 2001.06.11: Add SELECT INTO(parse_select_fetch_into). * 2001.06.11: Add PGBASH_DESCRIPTION table. * * *------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include "libpq-fe.h" #include "psql_sub.h" #include "sql_fetch.h" #include "utils.h" #include "sql_errno.h" #include "sql_set.h" #define PGBASH_DESCRIPTION "pgbash_description" #define PGBASH_DESCRIPTION_OBJOID_INDEX "pgbash_description_index" /*------------------------------------------------------------------ * Function List *------------------------------------------------------------------ * bool SendQuery(PSconnSet *pset, char *query); * int lo_exec(char *prog_name, char *parameter); */ static bool handleCopyOut(PGconn * conn, FILE * copystream); static bool handleCopyIn(PGconn * conn, const bool mustprompt, FILE * copystream); static bool do_lo_export(PSconnSet *pset, const char *loid_arg, const char *filename_arg); static int do_lo_import(PSconnSet *pset, const char *filename_arg, const char *comment_arg); static bool do_lo_unlink(PSconnSet *pset, const char *loid_arg); static bool param_lo_export(char *parameter, char *oid, char *filenm); static bool param_lo_import(char *parameter, char *filenm, char *comment); static bool param_lo_unlink(char *parameter, char *oid); static bool create_pgbash_description(PSconnSet *pset); /* * ################################################################ * psql_sub routines * ################################################################ */ bool SendQuery(PSconnSet * pset, char *query) /******************************************************************* * send the query string to the backend. * * return(true) : normal end * return(false) : error ******************************************************************/ { bool success = false; PGresult *results; PGnotify *notify; FILE *fout = pset->fout; PSfetchList *pfet = NULL; int fetflg = -1; const char *r_oid = NULL; int query_end = 0; /*-------- if query is "END" statement -----------*/ if( strUcmp(query, "END", 3 ) == 0 ) { query_end = 1; /* SQLCA is not set */ } /*-------- echo query sent to the backend --------*/ if (pset->echoQuery) { fprintf(fout, "########## QUERY ##########%s\n%s%s\n", get_CGI_BR(), query, get_CGI_BR()); fprintf(fout, "###########################%s\n", get_CGI_BR()); fprintf(fout, "%s\n", get_CGI_BR()); fflush(fout); } /*-------------- SELECT/FETCH INTO ---------------*/ fetflg = parse_select_fetch_into(query, &pfet); /*-------- clear OID shell variable --------------*/ bind_variable(SQLOID, ""); /*-------------- execute query -------------------*/ results = PQexec(pset->dbset->db, query); /*-------------- query error ---------------------*/ if (results == NULL) { setSQLCA(pset->sqlca, SQL_BAD_RESPONSE, PQerrorMessage(pset->dbset->db), 0, 0); printSQLerror(get_Ferr(), pset->sqlca); success = false; return success; } /*-------------- query OK! -----------------------*/ switch (PQresultStatus(results)) { case PGRES_TUPLES_OK: if( query_end == 0 ) setSQLCA(pset->sqlca, SQL_OK, "", PQntuples(results), PQnfields(results)); success = true; /*-------- SELECT query -------------*/ if (fetflg == -1 || PQntuples(results) > 1) { /*---- print out ------*/ alloc_printOpt(&(pset->opt), results); PSprint(pset->fout, &(pset->opt), pset->dbset->encoding); free_printOpt(&(pset->opt)); fflush(pset->fout); } /*--------- SELECT/FETCH INTO --------*/ else { if (exec_fetchInto(pset, results, pfet) == 0) (void) free_PSfetchList(pfet); else success = false; } /*---- set Field Name to SHELL var ---*/ if (PQnfields(results) >= 1) { int i; for (i = 0; i < PQnfields(results); i++) bind_array_variable("SQLFIELDNAME", i, PQfname(results, i)); } break; case PGRES_EMPTY_QUERY: if( query_end == 0 ) setSQLCA(pset->sqlca, SQL_EMPTY_QUERY, "", PQntuples(results), PQnfields(results)); success = true; break; case PGRES_COMMAND_OK: if( query_end == 0 ) setSQLCA(pset->sqlca, SQL_OK, "", PQntuples(results), PQnfields(results)); /*-------- OID of recent insert -----*/ r_oid = PQoidStatus(results); if (r_oid != NULL && r_oid[0] != '\0') bind_variable(SQLOID, (char *) r_oid); /*-------- set client_encoding to local DB pointer -----*/ set_client_encoding(pset, query); success = true; if (!pset->quiet && !pset->notty) printf("%s%s\n", PQcmdStatus(results), get_CGI_BR()); break; case PGRES_COPY_OUT: /* copy TABLE to STDOUT; */ setSQLCA(pset->sqlca, SQL_OK, "", PQntuples(results), PQnfields(results)); success = handleCopyOut(pset->dbset->db, pset->fout); if( !success ) setSQLCA(pset->sqlca, SQL_INVALID_DATA, PQerrorMessage(pset->dbset->db), 0, 0); fflush(pset->fout); break; case PGRES_COPY_IN: /* copy TABLE from STDIN; */ setSQLCA(pset->sqlca, SQL_OK, "", PQntuples(results), PQnfields(results)); if (!pset->quiet && !pset->notty) success = handleCopyIn(pset->dbset->db, true, stdin); else success = handleCopyIn(pset->dbset->db, false, stdin); if( !success ) setSQLCA(pset->sqlca, SQL_INVALID_DATA, PQerrorMessage(pset->dbset->db), 0, 0); break; case PGRES_NONFATAL_ERROR: setSQLCA(pset->sqlca, SQL_NONFATAL_ERROR, PQerrorMessage(pset->dbset->db), 0, 0); if (!pset->quiet) printSQLerror(pset->fout, pset->sqlca); success = false; break; case PGRES_FATAL_ERROR: setSQLCA(pset->sqlca, SQL_FATAL_ERROR, PQerrorMessage(pset->dbset->db), 0, 0); if (!pset->quiet) { if (strstr(pset->sqlca->sqlerrm.sqlerrmc, "parser") != NULL) fprintf(pset->fout, "Error SQL :%s\n%s%s\n%s\n", get_CGI_BR(), query, get_CGI_BR(), get_CGI_BR()); printSQLerror(pset->fout, pset->sqlca); } success = false; break; case PGRES_BAD_RESPONSE: setSQLCA(pset->sqlca, SQL_BAD_RESPONSE, PQerrorMessage(pset->dbset->db), 0, 0); if (!pset->quiet) printSQLerror(pset->fout, pset->sqlca); success = false; break; } /*------------- connection bad ----------------------*/ if (PQstatus(pset->dbset->db) == CONNECTION_BAD) { setSQLCA(pset->sqlca, SQL_CONNECTION_BAD, "\ We have lost the connection to the backend, so\n\ further processing is impossible. \n\ Terminating.", 0, 0); printSQLerror(get_Ferr(), pset->sqlca); /*******/ exit(2); /*******/ } /*---------- check for asynchronous returns ----------*/ while ((notify = PQnotifies(pset->dbset->db)) != NULL) { char buf[512]; sprintf(buf, "ASYNC NOTIFY of '%s' from backend pid '%d' received.", notify->relname, notify->be_pid); setSQLCA(pset->sqlca, SQL_CONNECTION_BAD, buf, 0, 0); printSQLerror(get_Ferr(), pset->sqlca); free(notify); } if (results) PQclear(results); return success; } /*########################################################################## * 'copy TABLE {to|from} {STDOUT|STDIN};' command * (if you use a 'copy TABLE(col1,..)' command, please look at "sql_copy.c") *########################################################################## */ static bool handleCopyOut(PGconn * conn, FILE * copystream) /****************************************************************** * copy TABLE to STDOUT; *****************************************************************/ { bool copydone; char copybuf[COPYBUFSIZ]; int ret; copydone = false; while (!copydone) { ret = PQgetline(conn, copybuf, COPYBUFSIZ); if (copybuf[0] == '\\' && copybuf[1] == '.' && copybuf[2] == '\0') { copydone = true; /* don't print this... */ } else { fputs(copybuf, copystream); switch (ret) { case EOF: copydone = true; /* FALLTHROUGH */ case 0: fputc('\n', copystream); break; case 1: break; } } } fflush(copystream); return !PQendcopy(conn); } static bool handleCopyIn(PGconn * conn, const bool mustprompt, FILE * copystream) /****************************************************************** * copy TABLE from STDIN; *****************************************************************/ { bool copydone = false; bool firstload; bool linedone; char copybuf[COPYBUFSIZ]; char *s; int buflen; int c = 0; if (mustprompt) { fputs("Enter data separated with a [Tab] followed by a newline\n", stdout); fputs("End with a '\\.' \n", stdout); } /*--------- for each input line -----------*/ while (!copydone) { if (mustprompt) { fputs(">> ", stdout); fflush(stdout); } firstload = true; linedone = false; /*----- for each buffer -----*/ while (!linedone) { s = copybuf; for (buflen = COPYBUFSIZ; buflen > 1; buflen--) { c = getc(copystream); if (c == '\n' || c == EOF) { linedone = true; break; } *s++ = c; } *s = '\0'; if (c == EOF) { PQputline(conn, "\\."); copydone = true; break; } PQputline(conn, copybuf); if (firstload) { if (!strcmp(copybuf, "\\.")) copydone = true; firstload = false; } } PQputline(conn, "\n"); } return !PQendcopy(conn); } /*########################################################################## * latge object routines *########################################################################## */ int lo_exec(PSconnSet *pset, char *prog_name, char *parameter) /************************************************************************* * Main routine for executing large_object routines. * * prog_name: e.g. "lo_export", "lo_import", "lo_unlink" * parameter: lo_import-- e.g. "'/tmp/test.dat', 'test file'" * lo_export-- e.g. "12345, '/tmp/test.dat'" * lo_unlink-- e.g. "12345" ************************************************************************/ { int i_oid; char oid[32]; char filenm[256]; char comment[256]; if( strcmp(prog_name,"lo_export")==0 ) { if(param_lo_export(parameter, oid, filenm)) { if(do_lo_export(pset, oid, filenm)) return(0); else return(-2); } else return(-1); } else if( strcmp(prog_name,"lo_import")==0 ) { if(param_lo_import(parameter, filenm, comment)) { if(i_oid=do_lo_import(pset, filenm, comment) > 0) { sprintf(oid, "%d", i_oid); bind_variable(SQLOID, oid); return(0); } else return(-2); } else return(-1); } else if( strcmp(prog_name,"lo_unlink")==0 ) { if(param_lo_unlink(parameter, oid)) { if(do_lo_unlink(pset, oid)) return(0); else return(-2); } else return(-1); } else return(-1); } bool do_lo_export(PSconnSet *pset, const char *loid_arg, const char *filename_arg) /*************************************************************************** * Write a large object(loid_arg) to a file **************************************************************************/ { PGresult *res; int status; bool own_transaction = true; if (own_transaction) { if (!SendQuery(pset, "BEGIN")) return false; } status = lo_export(pset->dbset->db, atol(loid_arg), filename_arg); if (status != 1) { /* of course this status is documented * nowhere :( */ setSQLCA(pset->sqlca, SQL_FATAL_ERROR, PQerrorMessage(pset->dbset->db), 0, 0); if (own_transaction) { res = PQexec(pset->dbset->db, "ROLLBACK"); PQclear(res); } return false; } if (own_transaction) { if (!SendQuery(pset, "COMMIT")) { res = PQexec(pset->dbset->db, "ROLLBACK"); PQclear(res); return false; } } return true; } int do_lo_import(PSconnSet *pset, const char *filename_arg, const char *comment_arg) /****************************************************************************** * Copy large object from file to database * * return value > 0 : OID * return value < 0 : error *****************************************************************************/ { PGresult *res; Oid loid; char buf[1024]; unsigned int i; bool own_transaction = true; /*----------- create PGBASH_DESCRIPTION table -----------*/ if (!pset->dbset->exist_des) { if (!create_pgbash_description(pset)) return -1; } /*----------- execute INSERT query ----------------------*/ if (own_transaction) { if (!SendQuery(pset, "BEGIN")) return -1; } loid = lo_import(pset->dbset->db, filename_arg); if (loid == 0) /* InvalidOid */ { setSQLCA(pset->sqlca, SQL_FATAL_ERROR, PQerrorMessage(pset->dbset->db), 0, 0); if (own_transaction) { res = PQexec(pset->dbset->db, "ROLLBACK"); PQclear(res); } return -1; } /* insert description if given */ if (comment_arg) { sprintf(buf, "INSERT INTO PGBASH_DESCRIPTION VALUES (%u, '", loid); for (i = 0; i < strlen(comment_arg); i++) if (comment_arg[i] == '\'') strcat(buf, "\\'"); else strncat(buf, &comment_arg[i], 1); strcat(buf, "')"); if (!SendQuery(pset, buf)) { if (own_transaction) { res = PQexec(pset->dbset->db, "ROLLBACK"); PQclear(res); } return -1; } } if (own_transaction) { if (!SendQuery(pset, "COMMIT")) { res = PQexec(pset->dbset->db, "ROLLBACK"); PQclear(res); return -1; } } return loid; } bool do_lo_unlink(PSconnSet *pset, const char *loid_arg) /********************************************************************* * removes a large object out of the database ********************************************************************/ { PGresult *res; int status; Oid loid = (Oid) atol(loid_arg); char buf[256]; bool own_transaction = true; if (own_transaction) { if (!SendQuery(pset, "BEGIN")) return false; } status = lo_unlink(pset->dbset->db, loid); if (status == -1) { setSQLCA(pset->sqlca, SQL_FATAL_ERROR, PQerrorMessage(pset->dbset->db), 0, 0); if (own_transaction) { res = PQexec(pset->dbset->db, "ROLLBACK"); PQclear(res); } return false; } /* remove the comment as well */ sprintf(buf, "DELETE FROM PGBASH_DESCRIPTION WHERE objoid = %u", loid); if (!SendQuery(pset, buf)) { if (own_transaction) { res = PQexec(pset->dbset->db, "ROLLBACK"); PQclear(res); } return false; } if (own_transaction) { if (!SendQuery(pset, "COMMIT")) { res = PQexec(pset->dbset->db, "ROLLBACK"); PQclear(res); return false; } } return true; } static bool param_lo_export(char *parameter, char *oid, char *filenm) /************************************************************** * parse parameter (parameter--> oid ,filnm) *************************************************************/ { char *p = parameter; char *p2; /*------- OID --------*/ p = skipSpace(p); p2= skipToken(p); if( p2-p > 32 || p2-p == 0 ) return(false); strncpy(oid, p, (p2-p)); oid[(p2-p)] = '\0'; /*------- ',' --------*/ p = skipSpace(p2); if( *p != ',' ) return(false); p++; /*------- '\'' --------*/ p = skipSpace(p); if ( *p == '\0' || *p != '\'' ) return(false); p++; p2 = strchr(p, '\''); if (p2 == NULL || *p2 != '\'' ) return(false); /*------- FILE name --------*/ if( p2-p > 255) return(false); strncpy(filenm, p, (p2-p)); filenm[(p2-p)] = '\0'; return(true); } static bool param_lo_import(char *parameter, char *filenm, char *comment) /************************************************************** * parse parameter (parameter--> filnm, comment) *************************************************************/ { char *p = parameter; char *p2; /*-------- FILE name --------*/ p = skipSpace(p); if ( *p == '\0' || *p != '\'' ) return(false); p++; p2 = strchr(p, '\''); if (p2 == NULL || *p2 != '\'' ) return(false); if( p2-p > 255 || p2-p == 0 ) return(false); strncpy(filenm, p, (p2-p)); filenm[(p2-p)] = '\0'; p2++; /*-------- ',' ------------*/ p = skipSpace(p2); if( *p != ',' ) return(false); p++; /*-------- COMMENT --------*/ p = skipSpace(p); if ( *p == '\0' || *p != '\'' ) return(false); p++; p2 = strchr(p, '\''); if (p2 == NULL || *p2 != '\'' ) return(false); if( p2-p > 255 || p2-p == 0 ) return(false); strncpy(comment, p, (p2-p)); comment[(p2-p)] = '\0'; return(true); } static bool param_lo_unlink(char *parameter, char *oid) /************************************************************** * parse parameter (parameter--> oid) *************************************************************/ { char *p = parameter; char *p2; /*-------- OID --------*/ p = skipSpace(p); p2= skipToken(p); if( p2-p > 32 || p2-p == 0 ) return(false); strncpy(oid, p, (p2-p)); oid[(p2-p)] = '\0'; return(true); } static bool create_pgbash_description(PSconnSet *pset) /************************************************************** * create PGBASH_DESCRIPTION table. *************************************************************/ { PGresult *res; char buf[256]; /*--------- select pgbash_description -----*/ strcpy(buf, "select count(*) from pg_class where relname='"); strcat(buf, PGBASH_DESCRIPTION); strcat(buf, "'"); res = PQexec(pset->dbset->db, buf); if (res == NULL) { setSQLCA(pset->sqlca, SQL_BAD_RESPONSE, PQerrorMessage(pset->dbset->db), 0, 0); printSQLerror(get_Ferr(), pset->sqlca); return false; } if (PQresultStatus(res) == PGRES_TUPLES_OK && atoi(PQgetvalue(res,0,0))==1) { PQclear(res); pset->dbset->exist_des = true; return true; } PQclear(res); /*--------- create CREATE query -----------*/ strcpy(buf, "Create Table "); strcat(buf, PGBASH_DESCRIPTION); strcat(buf, "(objoid oid, description text)"); /*---------- execute CREATE TABLE ---------*/ res = PQexec(pset->dbset->db, buf); if (res == NULL) { setSQLCA(pset->sqlca, SQL_BAD_RESPONSE, PQerrorMessage(pset->dbset->db), 0, 0); printSQLerror(get_Ferr(), pset->sqlca); return false; } if (PQresultStatus(res) != PGRES_COMMAND_OK) { setSQLCA(pset->sqlca, SQL_BAD_RESPONSE, PQerrorMessage(pset->dbset->db), 0, 0); printSQLerror(get_Ferr(), pset->sqlca); return false; } PQclear(res); /*--------- craete INDEX ------------------*/ strcpy(buf, "Create Index "); strcat(buf, PGBASH_DESCRIPTION_OBJOID_INDEX); strcat(buf, " on "); strcat(buf, PGBASH_DESCRIPTION); strcat(buf, "(objoid)"); /*---------- execute CREATE TABLE ---------*/ res = PQexec(pset->dbset->db, buf); if (res == NULL) { setSQLCA(pset->sqlca, SQL_BAD_RESPONSE, PQerrorMessage(pset->dbset->db), 0, 0); printSQLerror(get_Ferr(), pset->sqlca); return false; } if (PQresultStatus(res) != PGRES_COMMAND_OK) { setSQLCA(pset->sqlca, SQL_BAD_RESPONSE, PQerrorMessage(pset->dbset->db), 0, 0); printSQLerror(get_Ferr(), pset->sqlca); return false; } PQclear(res); pset->dbset->exist_des = true; return true; }