From b54befd7bcc6530f7674cfec4524df9341de2447 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sun, 6 Nov 2022 11:33:59 -0800 Subject: [PATCH] GET DATA: Improve coding style and tests. --- src/data/psql-reader.c | 26 +-- src/data/psql-reader.h | 2 +- src/language/data-io/get-data.c | 346 ++++++++++++----------------- tests/automake.mk | 1 + tests/language/data-io/get-data.at | 302 +++++++++++++++++++++++++ 5 files changed, 460 insertions(+), 217 deletions(-) create mode 100644 tests/language/data-io/get-data.at diff --git a/src/data/psql-reader.c b/src/data/psql-reader.c index ae56d18212..e372a859a9 100644 --- a/src/data/psql-reader.c +++ b/src/data/psql-reader.c @@ -234,7 +234,6 @@ psql_open_reader (struct psql_read_info *info, struct dictionary **dict) const char *encoding; struct psql_reader *r = XZALLOC (struct psql_reader); - struct string query ; r->conn = PQconnectdb (info->conninfo); if (NULL == r->conn) @@ -305,21 +304,19 @@ psql_open_reader (struct psql_read_info *info, struct dictionary **dict) } const int version = PQserverVersion (r->conn); - ds_init_empty (&query); /* Versions before 9.1 don't have the REPEATABLE READ isolation level. However according to if the server is in the "hot standby" mode then SERIALIZABLE won't work. */ - ds_put_c_format (&query, - "BEGIN READ ONLY ISOLATION LEVEL %s; " - "DECLARE pspp BINARY CURSOR FOR ", - (version < 90100) ? "SERIALIZABLE" : "REPEATABLE READ"); + char *query = xasprintf ( + "BEGIN READ ONLY ISOLATION LEVEL %s; " + "DECLARE pspp BINARY CURSOR FOR %s", + (version < 90100) ? "SERIALIZABLE" : "REPEATABLE READ", + info->sql); + qres = PQexec (r->conn, query); + free (query); - ds_put_substring (&query, info->sql.ss); - - qres = PQexec (r->conn, ds_cstr (&query)); - ds_destroy (&query); if (PQresultStatus (qres) != PGRES_COMMAND_OK) { msg (ME, _("Error from psql source: %s."), @@ -339,12 +336,11 @@ psql_open_reader (struct psql_read_info *info, struct dictionary **dict) On the other hand, most PSPP functions don't need to know this. The GUI is the notable exception. */ - ds_init_cstr (&query, "SELECT count (*) FROM ("); - ds_put_substring (&query, info->sql.ss); - ds_put_cstr (&query, ") stupid_sql_standard"); + query = xasprintf ("SELECT count (*) FROM (%s) stupid_sql_standard", + info->sql); + qres = PQexec (r->conn, query); + free (query); - qres = PQexec (r->conn, ds_cstr (&query)); - ds_destroy (&query); if (PQresultStatus (qres) != PGRES_TUPLES_OK) { msg (ME, _("Error from psql source: %s."), diff --git a/src/data/psql-reader.h b/src/data/psql-reader.h index 8fd584c60c..685d19a918 100644 --- a/src/data/psql-reader.h +++ b/src/data/psql-reader.h @@ -25,7 +25,7 @@ struct casereader; struct psql_read_info { char *conninfo ; - struct string sql; + char * sql; bool allow_clear; int str_width; int bsize; diff --git a/src/language/data-io/get-data.c b/src/language/data-io/get-data.c index 4474118166..f35cb2f2aa 100644 --- a/src/language/data-io/get-data.c +++ b/src/language/data-io/get-data.c @@ -51,110 +51,78 @@ static bool parse_spreadsheet (struct lexer *lexer, char **filename, static void destroy_spreadsheet_read_info (struct spreadsheet_read_options *); -static int parse_get_txt (struct lexer *lexer, struct dataset *); -static int parse_get_psql (struct lexer *lexer, struct dataset *); +static int parse_get_txt (struct lexer *, struct dataset *); +static int parse_get_psql (struct lexer *, struct dataset *); +static int parse_get_spreadsheet (struct lexer *, struct dataset *, + struct spreadsheet *(*probe)( + const char *filename, bool report_errors)); int cmd_get_data (struct lexer *lexer, struct dataset *ds) { - char *tok = NULL; - struct spreadsheet_read_options opts; - - opts.sheet_name = NULL; - opts.sheet_index = -1; - opts.cell_range = NULL; - opts.read_names = false; - opts.asw = -1; - - if (! lex_force_match (lexer, T_SLASH)) - goto error; - - if (!lex_force_match_id (lexer, "TYPE")) - goto error; - - if (!lex_force_match (lexer, T_EQUALS)) - goto error; - - const char *s = lex_tokcstr (lexer); - - if (s) - tok = strdup (s); + if (!lex_force_match_phrase (lexer, "/TYPE=")) + return CMD_FAILURE; if (lex_match_id (lexer, "TXT")) - { - free (tok); - return parse_get_txt (lexer, ds); - } + return parse_get_txt (lexer, ds); else if (lex_match_id (lexer, "PSQL")) + return parse_get_psql (lexer, ds); + else if (lex_match_id (lexer, "GNM")) + return parse_get_spreadsheet (lexer, ds, gnumeric_probe); + else if (lex_match_id (lexer, "ODS")) + return parse_get_spreadsheet (lexer, ds, ods_probe); + else { - free (tok); - return parse_get_psql (lexer, ds); + lex_error_expecting (lexer, "TXT", "PSQL", "GNM", "ODS"); + return CMD_FAILURE; } - else if (lex_match_id (lexer, "GNM") || - lex_match_id (lexer, "ODS")) - { - char *filename = NULL; - if (!parse_spreadsheet (lexer, &filename, &opts)) - goto error; +} - struct spreadsheet *spreadsheet = NULL; - if (0 == strncasecmp (tok, "GNM", 3)) - spreadsheet = gnumeric_probe (filename, true); - else if (0 == strncasecmp (tok, "ODS", 3)) - spreadsheet = ods_probe (filename, true); +static int +parse_get_spreadsheet (struct lexer *lexer, struct dataset *ds, + struct spreadsheet *(*probe)( + const char *filename, bool report_errors)) +{ + struct spreadsheet_read_options opts; + char *filename; + if (!parse_spreadsheet (lexer, &filename, &opts)) + return CMD_FAILURE; - if (spreadsheet == NULL) - { - msg (SE, _("error reading file `%s'"), filename); - free (filename); - goto error; - } - free (filename); + bool ok = false; + struct spreadsheet *spreadsheet = probe (filename, true); + if (!spreadsheet) + { + msg (SE, _("error reading file `%s'"), filename); + goto done; + } - struct casereader *reader = spreadsheet_make_reader (spreadsheet, &opts); - if (reader) - { - dataset_set_dict (ds, dict_clone (spreadsheet->dict)); - dataset_set_source (ds, reader); - free (tok); - destroy_spreadsheet_read_info (&opts); - spreadsheet_unref (spreadsheet); - return CMD_SUCCESS; - } - spreadsheet_unref (spreadsheet); + struct casereader *reader = spreadsheet_make_reader (spreadsheet, &opts); + if (reader) + { + dataset_set_dict (ds, dict_clone (spreadsheet->dict)); + dataset_set_source (ds, reader); + ok = true; } - else - lex_error_expecting (lexer, "TXT", "PSQL", "GNM", "ODS"); + spreadsheet_unref (spreadsheet); - error: +done: + free (filename); destroy_spreadsheet_read_info (&opts); - free (tok); - return CMD_FAILURE; + return ok ? CMD_SUCCESS : CMD_FAILURE; } static int parse_get_psql (struct lexer *lexer, struct dataset *ds) { - struct psql_read_info psql; - psql.allow_clear = false; - psql.conninfo = NULL; - psql.str_width = -1; - psql.bsize = -1; - ds_init_empty (&psql.sql); - - if (! lex_force_match (lexer, T_SLASH)) - goto error; - - if (!lex_force_match_id (lexer, "CONNECT")) - goto error; - - if (! lex_force_match (lexer, T_EQUALS)) - goto error; - - if (!lex_force_string (lexer)) - goto error; + if (!lex_force_match_phrase (lexer, "/CONNECT=") || !lex_force_string (lexer)) + return CMD_FAILURE; - psql.conninfo = ss_xstrdup (lex_tokss (lexer)); + struct psql_read_info psql = { + .str_width = -1, + .bsize = -1, + .conninfo = ss_xstrdup (lex_tokss (lexer)), + }; + bool ok = false; lex_get (lexer); @@ -163,83 +131,63 @@ parse_get_psql (struct lexer *lexer, struct dataset *ds) if (lex_match_id (lexer, "ASSUMEDSTRWIDTH")) { lex_match (lexer, T_EQUALS); - if (lex_force_int_range (lexer, "ASSUMEDSTRWIDTH", 1, 32767)) - { - psql.str_width = lex_integer (lexer); - lex_get (lexer); - } + if (!lex_force_int_range (lexer, "ASSUMEDSTRWIDTH", 1, 32767)) + goto done; + psql.str_width = lex_integer (lexer); + lex_get (lexer); } else if (lex_match_id (lexer, "BSIZE")) { lex_match (lexer, T_EQUALS); - if (lex_force_int_range (lexer, "BSIZE", 1, INT_MAX)) - { - psql.bsize = lex_integer (lexer); - lex_get (lexer); - } + if (!lex_force_int_range (lexer, "BSIZE", 1, INT_MAX)) + goto done; + psql.bsize = lex_integer (lexer); + lex_get (lexer); } else if (lex_match_id (lexer, "UNENCRYPTED")) - { - psql.allow_clear = true; - } + psql.allow_clear = true; else if (lex_match_id (lexer, "SQL")) { lex_match (lexer, T_EQUALS); - if (! lex_force_string (lexer)) - goto error; + if (!lex_force_string (lexer)) + goto done; - ds_put_substring (&psql.sql, lex_tokss (lexer)); + free (psql.sql); + psql.sql = ss_xstrdup (lex_tokss (lexer)); lex_get (lexer); } } - { - struct dictionary *dict = NULL; - struct casereader *reader = psql_open_reader (&psql, &dict); - - if (reader) - { - dataset_set_dict (ds, dict); - dataset_set_source (ds, reader); - } - } - - ds_destroy (&psql.sql); - free (psql.conninfo); - return CMD_SUCCESS; - - error: + struct dictionary *dict = NULL; + struct casereader *reader = psql_open_reader (&psql, &dict); + if (reader) + { + dataset_set_dict (ds, dict); + dataset_set_source (ds, reader); + } - ds_destroy (&psql.sql); + done: free (psql.conninfo); + free (psql.sql); - return CMD_FAILURE; + return ok ? CMD_SUCCESS : CMD_FAILURE; } static bool parse_spreadsheet (struct lexer *lexer, char **filename, struct spreadsheet_read_options *opts) { - opts->sheet_index = 1; - opts->sheet_name = NULL; - opts->cell_range = NULL; - opts->read_names = true; - opts->asw = -1; - - if (! lex_force_match (lexer, T_SLASH)) + *opts = (struct spreadsheet_read_options) { + .sheet_index = 1, + .read_names = true, + .asw = -1, + }; + *filename = NULL; + + if (!lex_force_match_phrase (lexer, "/FILE=") || !lex_force_string (lexer)) goto error; - if (!lex_force_match_id (lexer, "FILE")) - goto error; - - if (! lex_force_match (lexer, T_EQUALS)) - goto error; - - if (!lex_force_string (lexer)) - goto error; - - *filename = utf8_to_filename (lex_tokcstr (lexer)); - + *filename = utf8_to_filename (lex_tokcstr (lexer)); lex_get (lexer); while (lex_match (lexer, T_SLASH)) @@ -247,18 +195,17 @@ parse_spreadsheet (struct lexer *lexer, char **filename, if (lex_match_id (lexer, "ASSUMEDSTRWIDTH")) { lex_match (lexer, T_EQUALS); - if (lex_force_int_range (lexer, "ASSUMEDSTRWIDTH", 1, 32767)) - { - opts->asw = lex_integer (lexer); - lex_get (lexer); - } + if (!lex_force_int_range (lexer, "ASSUMEDSTRWIDTH", 1, 32767)) + goto error; + opts->asw = lex_integer (lexer); + lex_get (lexer); } else if (lex_match_id (lexer, "SHEET")) { lex_match (lexer, T_EQUALS); if (lex_match_id (lexer, "NAME")) { - if (! lex_force_string (lexer)) + if (!lex_force_string (lexer)) goto error; opts->sheet_name = ss_xstrdup (lex_tokss (lexer)); @@ -284,12 +231,10 @@ parse_spreadsheet (struct lexer *lexer, char **filename, lex_match (lexer, T_EQUALS); if (lex_match_id (lexer, "FULL")) - { - opts->cell_range = NULL; - } + opts->cell_range = NULL; else if (lex_match_id (lexer, "RANGE")) { - if (! lex_force_string (lexer)) + if (!lex_force_string (lexer)) goto error; opts->cell_range = ss_xstrdup (lex_tokss (lexer)); @@ -306,13 +251,9 @@ parse_spreadsheet (struct lexer *lexer, char **filename, lex_match (lexer, T_EQUALS); if (lex_match_id (lexer, "ON")) - { - opts->read_names = true; - } + opts->read_names = true; else if (lex_match_id (lexer, "OFF")) - { - opts->read_names = false; - } + opts->read_names = false; else { lex_error_expecting (lexer, "ON", "OFF"); @@ -321,7 +262,8 @@ parse_spreadsheet (struct lexer *lexer, char **filename, } else { - lex_error (lexer, NULL); + lex_error_expecting (lexer, "ASSUMEDSTRWIDTH", "SHEET", "CELLRANGE", + "READNAMES"); goto error; } } @@ -329,26 +271,32 @@ parse_spreadsheet (struct lexer *lexer, char **filename, return true; error: + destroy_spreadsheet_read_info (opts); + free (*filename); return false; } static bool -set_type (struct data_parser *parser, const char *subcommand, - enum data_parser_type type, bool *has_type) +set_type (struct lexer *lexer, struct data_parser *parser, + enum data_parser_type type, + int type_start, int type_end, int *type_startp, int *type_endp) { - if (!*has_type) + if (!*type_startp) { data_parser_set_type (parser, type); - *has_type = true; + *type_startp = type_start; + *type_endp = type_end; } else if (type != data_parser_get_type (parser)) { - msg (SE, _("%s is allowed only with %s arrangement, but %s arrangement " - "was stated or implied earlier in this command."), - subcommand, - type == DP_FIXED ? "FIXED" : "DELIMITED", - type == DP_FIXED ? "DELIMITED" : "FIXED"); + msg (SE, _("FIXED and DELIMITED arrangements are mutually exclusive.")); + lex_ofs_msg (lexer, SN, type_start, type_end, + _("This syntax requires %s arrangement."), + type == DP_FIXED ? "FIXED" : "DELIMITED"); + lex_ofs_msg (lexer, SN, *type_startp, *type_endp, + _("This syntax requires %s arrangement."), + type == DP_FIXED ? "DELIMITED" : "FIXED"); return false; } return true; @@ -357,30 +305,19 @@ set_type (struct data_parser *parser, const char *subcommand, static int parse_get_txt (struct lexer *lexer, struct dataset *ds) { - struct data_parser *parser = NULL; struct dictionary *dict = dict_create (get_default_encoding ()); + struct data_parser *parser = data_parser_create (); struct file_handle *fh = NULL; - struct dfm_reader *reader = NULL; char *encoding = NULL; char *name = NULL; - int record; - enum data_parser_type type; - bool has_type; - - if (! lex_force_match (lexer, T_SLASH)) - goto error; - - if (!lex_force_match_id (lexer, "FILE")) - goto error; - if (! lex_force_match (lexer, T_EQUALS)) + if (!lex_force_match_phrase (lexer, "/FILE=")) goto error; fh = fh_parse (lexer, FH_REF_FILE | FH_REF_INLINE, NULL); if (fh == NULL) goto error; - parser = data_parser_create (); - has_type = false; + int type_start = 0, type_end = 0; data_parser_set_type (parser, DP_DELIMITED); data_parser_set_span (parser, false); data_parser_set_quotes (parser, ss_empty ()); @@ -409,10 +346,13 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds) lex_match (lexer, T_EQUALS); if (lex_match_id (lexer, "FIXED")) - ok = set_type (parser, "ARRANGEMENT=FIXED", DP_FIXED, &has_type); + ok = set_type (lexer, parser, DP_FIXED, + lex_ofs (lexer) - 3, lex_ofs (lexer) - 1, + &type_start, &type_end); else if (lex_match_id (lexer, "DELIMITED")) - ok = set_type (parser, "ARRANGEMENT=DELIMITED", - DP_DELIMITED, &has_type); + ok = set_type (lexer, parser, DP_DELIMITED, + lex_ofs (lexer) - 3, lex_ofs (lexer) - 1, + &type_start, &type_end); else { lex_error_expecting (lexer, "FIXED", "DELIMITED"); @@ -431,7 +371,9 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds) } else if (lex_match_id_n (lexer, "DELCASE", 4)) { - if (!set_type (parser, "DELCASE", DP_DELIMITED, &has_type)) + if (!set_type (lexer, parser, DP_DELIMITED, + lex_ofs (lexer) - 1, lex_ofs (lexer) - 1, + &type_start, &type_end)) goto error; lex_match (lexer, T_EQUALS); if (lex_match_id (lexer, "LINE")) @@ -454,7 +396,9 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds) } else if (lex_match_id (lexer, "FIXCASE")) { - if (!set_type (parser, "FIXCASE", DP_FIXED, &has_type)) + if (!set_type (lexer, parser, DP_FIXED, + lex_ofs (lexer) - 1, lex_ofs (lexer) - 1, + &type_start, &type_end)) goto error; lex_match (lexer, T_EQUALS); if (!lex_force_int_range (lexer, "FIXCASE", 1, INT_MAX)) @@ -488,12 +432,9 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds) } else if (lex_match_id_n (lexer, "DELIMITERS", 4)) { - struct string hard_seps = DS_EMPTY_INITIALIZER; - const char *soft_seps = ""; - struct substring s; - int c; - - if (!set_type (parser, "DELIMITERS", DP_DELIMITED, &has_type)) + if (!set_type (lexer, parser, DP_DELIMITED, + lex_ofs (lexer) - 1, lex_ofs (lexer) - 1, + &type_start, &type_end)) goto error; lex_match (lexer, T_EQUALS); @@ -501,11 +442,14 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds) goto error; /* XXX should support multibyte UTF-8 characters */ - s = lex_tokss (lexer); + struct substring s = lex_tokss (lexer); + struct string hard_seps = DS_EMPTY_INITIALIZER; + const char *soft_seps = ""; if (ss_match_string (&s, ss_cstr ("\\t"))) ds_put_cstr (&hard_seps, "\t"); if (ss_match_string (&s, ss_cstr ("\\\\"))) ds_put_cstr (&hard_seps, "\\"); + int c; while ((c = ss_get_byte (&s)) != EOF) if (c == ' ') soft_seps = " "; @@ -519,7 +463,9 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds) } else if (lex_match_id (lexer, "QUALIFIERS")) { - if (!set_type (parser, "QUALIFIERS", DP_DELIMITED, &has_type)) + if (!set_type (lexer, parser, DP_DELIMITED, + lex_ofs (lexer) - 1, lex_ofs (lexer) - 1, + &type_start, &type_end)) goto error; lex_match (lexer, T_EQUALS); @@ -548,14 +494,10 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds) } lex_match (lexer, T_EQUALS); - record = 1; - type = data_parser_get_type (parser); + int record = 1; + enum data_parser_type type = data_parser_get_type (parser); do { - struct fmt_spec input, output; - struct variable *v; - int fc, lc; - while (type == DP_FIXED && lex_match (lexer, T_SLASH)) { if (!lex_force_int_range (lexer, NULL, record, @@ -577,6 +519,9 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds) goto error; } lex_get (lexer); + + struct fmt_spec input, output; + int fc, lc; if (type == DP_DELIMITED) { if (!parse_format_specifier (lexer, &input)) @@ -618,7 +563,7 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds) error = fmt_check_input__ (&input); if (error) { - lex_next_error (lexer, start_ofs, end_ofs, "%s", error); + lex_ofs_error (lexer, start_ofs, end_ofs, "%s", error); free (error); goto error; } @@ -630,7 +575,7 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds) error = fmt_check_output__ (&output); if (error) { - lex_next_error (lexer, start_ofs, end_ofs, "%s", error); + lex_ofs_error (lexer, start_ofs, end_ofs, "%s", error); free (error); goto error; } @@ -639,8 +584,8 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds) output = fmt_for_output_from_input (&input, settings_get_fmt_settings ()); } - v = dict_create_var (dict, name, fmt_var_width (&input)); - if (v == NULL) + struct variable *v = dict_create_var (dict, name, fmt_var_width (&input)); + if (!v) { lex_ofs_error (lexer, name_ofs, name_ofs, _("%s is a duplicate variable name."), name); @@ -659,8 +604,8 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds) } while (lex_token (lexer) != T_ENDCMD); - reader = dfm_open_reader (fh, lexer, encoding); - if (reader == NULL) + struct dfm_reader *reader = dfm_open_reader (fh, lexer, encoding); + if (!reader) goto error; data_parser_make_active_file (parser, ds, reader, dict, NULL, NULL); @@ -677,7 +622,6 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds) return CMD_CASCADING_FAILURE; } - static void destroy_spreadsheet_read_info (struct spreadsheet_read_options *opts) { diff --git a/tests/automake.mk b/tests/automake.mk index 6cfb3e0eee..679be89537 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -357,6 +357,7 @@ TESTSUITE_AT = \ tests/language/data-io/data-reader.at \ tests/language/data-io/dataset.at \ tests/language/data-io/file-handle.at \ + tests/language/data-io/get-data.at \ tests/language/data-io/get-data-spreadsheet.at \ tests/language/data-io/get-data-psql.at \ tests/language/data-io/get-data-txt.at \ diff --git a/tests/language/data-io/get-data.at b/tests/language/data-io/get-data.at new file mode 100644 index 0000000000..1785b5b3cd --- /dev/null +++ b/tests/language/data-io/get-data.at @@ -0,0 +1,302 @@ +AT_BANNER([GET DATA]) + +AT_SETUP([GET DATA syntax errors]) +AT_DATA([get-data.sps], [dnl +GET DATA **. +GET DATA / **. +GET DATA /TYPE **. + +GET DATA /TYPE=TXT **. +GET DATA /TYPE=TXT/ **. +GET DATA /TYPE=TXT/FILE **. +GET DATA /TYPE=TXT/FILE='x.txt' **. +GET DATA /TYPE=TXT/FILE='x.txt' /ENCODING=**. +GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=**. +GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /ARRANGEMENT=DELIMITED. +GET DATA /TYPE=TXT/FILE='x.txt' /FIRSTCASE=**. +GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /DELCASE=LINE. +GET DATA /TYPE=TXT/FILE='x.txt' /DELCASE=VARIABLES **. +GET DATA /TYPE=TXT/FILE='x.txt' /DELCASE=**. +GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=DELIMITED /FIXCASE=1. +GET DATA /TYPE=TXT/FILE='x.txt' /FIXCASE=**. +GET DATA /TYPE=TXT/FILE='x.txt' /IMPORTCASES=FIRST **. +GET DATA /TYPE=TXT/FILE='x.txt' /IMPORTCASES=PERCENT **. +GET DATA /TYPE=TXT/FILE='x.txt' /IMPORTCASES=ALL. +GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /DELIMITERS=' '. +GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /QUALIFIER='"'. +GET DATA /TYPE=TXT/FILE='x.txt' /QUALIFIER='"' + "'". +GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /VARIABLES / **. +GET DATA /TYPE=TXT/FILE='x.txt' /VARIABLES **. +GET DATA /TYPE=TXT/FILE='x.txt' /VARIABLES a_very_long_name_that_exceeds_the_64_byte_limit_for_variable_names. +GET DATA /TYPE=TXT/FILE='x.txt' /VARIABLES x **. +GET DATA /TYPE=TXT/FILE='x.txt' /VARIABLES x F1.2. +GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /VARIABLES x **. +GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /VARIABLES x 1-5 **. +GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /VARIABLES x 1-5 FOO. +GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /VARIABLES x 1-5 DATE. +GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /VARIABLES x 1-5 DOLLAR1.2. +GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /VARIABLES x 1-5 F x 6-10 F. + +GET DATA /TYPE=PSQL **. +GET DATA /TYPE=PSQL/ **. +GET DATA /TYPE=PSQL/CONNECT **. +GET DATA /TYPE=PSQL/CONNECT='db'/ASSUMEDSTRWIDTH=**. +GET DATA /TYPE=PSQL/CONNECT='db'/BSIZE=**. +GET DATA /TYPE=PSQL/CONNECT='db'/SQL=**. + +GET DATA /TYPE=GNM **. +GET DATA /TYPE=GNM/ **. +GET DATA /TYPE=GNM/FILE **. +GET DATA /TYPE=GNM/FILE= **. +GET DATA /TYPE=GNM/FILE='x.gnumeric'/ASSUMEDSTRWIDTH=**. +GET DATA /TYPE=GNM/FILE='x.gnumeric'/SHEET=NAME **. +GET DATA /TYPE=GNM/FILE='x.gnumeric'/SHEET=INDEX **. +GET DATA /TYPE=GNM/FILE='x.gnumeric'/SHEET=**. +GET DATA /TYPE=GNM/FILE='x.gnumeric'/CELLRANGE=RANGE **. +GET DATA /TYPE=GNM/FILE='x.gnumeric'/CELLRANGE=**. +GET DATA /TYPE=GNM/FILE='x.gnumeric'/READNAMES=**. +GET DATA /TYPE=GNM/FILE='x.gnumeric'/ **. +]) +AT_DATA([insert.sps], [dnl +INSERT FILE='get-data.sps' ERROR=IGNORE. +]) +AT_CHECK([pspp -x compatible --testing-mode -O format=csv insert.sps], [1], [dnl +"get-data.sps:1.10-1.11: error: GET DATA: Syntax error expecting `/TYPE='. + 1 | GET DATA **. + | ^~" + +"get-data.sps:2.10-2.13: error: GET DATA: Syntax error expecting `/TYPE='. + 2 | GET DATA / **. + | ^~~~" + +"get-data.sps:3.10-3.17: error: GET DATA: Syntax error expecting `/TYPE='. + 3 | GET DATA /TYPE **. + | ^~~~~~~~" + +"get-data.sps:5.20-5.21: error: GET DATA: Syntax error expecting `/FILE='. + 5 | GET DATA /TYPE=TXT **. + | ^~" + +"get-data.sps:6.19-6.22: error: GET DATA: Syntax error expecting `/FILE='. + 6 | GET DATA /TYPE=TXT/ **. + | ^~~~" + +"get-data.sps:7.19-7.26: error: GET DATA: Syntax error expecting `/FILE='. + 7 | GET DATA /TYPE=TXT/FILE **. + | ^~~~~~~~" + +"get-data.sps:8.33-8.34: error: GET DATA: Syntax error expecting `/'. + 8 | GET DATA /TYPE=TXT/FILE='x.txt' **. + | ^~" + +"get-data.sps:9.43-9.44: error: GET DATA: Syntax error expecting string. + 9 | GET DATA /TYPE=TXT/FILE='x.txt' /ENCODING=**. + | ^~" + +"get-data.sps:10.46-10.47: error: GET DATA: Syntax error expecting FIXED or DELIMITED. + 10 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=**. + | ^~" + +get-data.sps:11: error: GET DATA: FIXED and DELIMITED arrangements are mutually exclusive. + +"get-data.sps:11.53-11.73: note: GET DATA: This syntax requires DELIMITED arrangement. + 11 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /ARRANGEMENT=DELIMITED. + | ^~~~~~~~~~~~~~~~~~~~~" + +"get-data.sps:11.34-11.50: note: GET DATA: This syntax requires FIXED arrangement. + 11 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /ARRANGEMENT=DELIMITED. + | ^~~~~~~~~~~~~~~~~" + +"get-data.sps:12.44-12.45: error: GET DATA: Syntax error expecting positive integer for FIRSTCASE. + 12 | GET DATA /TYPE=TXT/FILE='x.txt' /FIRSTCASE=**. + | ^~" + +get-data.sps:13: error: GET DATA: FIXED and DELIMITED arrangements are mutually exclusive. + +"get-data.sps:13.53-13.59: note: GET DATA: This syntax requires DELIMITED arrangement. + 13 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /DELCASE=LINE. + | ^~~~~~~" + +"get-data.sps:13.34-13.50: note: GET DATA: This syntax requires FIXED arrangement. + 13 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /DELCASE=LINE. + | ^~~~~~~~~~~~~~~~~" + +"get-data.sps:14.52-14.53: error: GET DATA: Syntax error expecting integer. + 14 | GET DATA /TYPE=TXT/FILE='x.txt' /DELCASE=VARIABLES **. + | ^~" + +"get-data.sps:15.42-15.43: error: GET DATA: Syntax error expecting LINE or VARIABLES. + 15 | GET DATA /TYPE=TXT/FILE='x.txt' /DELCASE=**. + | ^~" + +get-data.sps:16: error: GET DATA: FIXED and DELIMITED arrangements are mutually exclusive. + +"get-data.sps:16.57-16.63: note: GET DATA: This syntax requires FIXED arrangement. + 16 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=DELIMITED /FIXCASE=1. + | ^~~~~~~" + +"get-data.sps:16.34-16.54: note: GET DATA: This syntax requires DELIMITED arrangement. + 16 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=DELIMITED /FIXCASE=1. + | ^~~~~~~~~~~~~~~~~~~~~" + +"get-data.sps:17.42-17.43: error: GET DATA: Syntax error expecting positive integer for FIXCASE. + 17 | GET DATA /TYPE=TXT/FILE='x.txt' /FIXCASE=**. + | ^~" + +"get-data.sps:18.52-18.53: error: GET DATA: Syntax error expecting integer. + 18 | GET DATA /TYPE=TXT/FILE='x.txt' /IMPORTCASES=FIRST **. + | ^~" + +"get-data.sps:19.54-19.55: error: GET DATA: Syntax error expecting integer. + 19 | GET DATA /TYPE=TXT/FILE='x.txt' /IMPORTCASES=PERCENT **. + | ^~" + +"get-data.sps:20.34-20.48: warning: GET DATA: Ignoring obsolete IMPORTCASES subcommand. (N OF CASES or SAMPLE may be used to substitute.). + 20 | GET DATA /TYPE=TXT/FILE='x.txt' /IMPORTCASES=ALL. + | ^~~~~~~~~~~~~~~" + +"get-data.sps:20.49: error: GET DATA: Syntax error expecting `/'. + 20 | GET DATA /TYPE=TXT/FILE='x.txt' /IMPORTCASES=ALL. + | ^" + +get-data.sps:21: error: GET DATA: FIXED and DELIMITED arrangements are mutually exclusive. + +"get-data.sps:21.53-21.62: note: GET DATA: This syntax requires DELIMITED arrangement. + 21 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /DELIMITERS=' '. + | ^~~~~~~~~~" + +"get-data.sps:21.34-21.50: note: GET DATA: This syntax requires FIXED arrangement. + 21 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /DELIMITERS=' '. + | ^~~~~~~~~~~~~~~~~" + +get-data.sps:22: error: GET DATA: FIXED and DELIMITED arrangements are mutually exclusive. + +"get-data.sps:22.53-22.61: note: GET DATA: This syntax requires DELIMITED arrangement. + 22 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /QUALIFIER='""'. + | ^~~~~~~~~" + +"get-data.sps:22.34-22.50: note: GET DATA: This syntax requires FIXED arrangement. + 22 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /QUALIFIER='""'. + | ^~~~~~~~~~~~~~~~~" + +"get-data.sps:23.44-23.52: error: GET DATA: In compatible syntax mode, the QUALIFIER string must contain exactly one character. + 23 | GET DATA /TYPE=TXT/FILE='x.txt' /QUALIFIER='""' + ""'"". + | ^~~~~~~~~" + +"get-data.sps:24.65-24.66: error: GET DATA: Syntax error expecting integer. + 24 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /VARIABLES / **. + | ^~" + +"get-data.sps:25.44-25.45: error: GET DATA: Syntax error expecting identifier. + 25 | GET DATA /TYPE=TXT/FILE='x.txt' /VARIABLES **. + | ^~" + +"get-data.sps:26.44-26.109: error: GET DATA: Identifier `a_very_long_name_that_exceeds_the_64_byte_limit_for_variable_names' exceeds 64-byte limit. + 26 | GET DATA /TYPE=TXT/FILE='x.txt' /VARIABLES a_very_long_name_that_exceeds_the_64_byte_limit_for_variable_names. + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + +"get-data.sps:27.46-27.47: error: GET DATA: Syntax error expecting valid format specifier. + 27 | GET DATA /TYPE=TXT/FILE='x.txt' /VARIABLES x **. + | ^~" + +"get-data.sps:28.46-28.49: error: GET DATA: Input format F1.2 specifies 2 decimal places, but width 1 allows at most 1 decimals. + 28 | GET DATA /TYPE=TXT/FILE='x.txt' /VARIABLES x F1.2. + | ^~~~" + +"get-data.sps:29.65-29.66: error: GET DATA: Syntax error expecting integer. + 29 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /VARIABLES x **. + | ^~" + +"get-data.sps:30.69-30.70: error: GET DATA: Syntax error expecting valid format specifier. + 30 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /VARIABLES x 1-5 **. + | ^~" + +"get-data.sps:31.69-31.71: error: GET DATA: Unknown format type `FOO'. + 31 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /VARIABLES x 1-5 FOO. + | ^~~" + +"get-data.sps:32.65-32.72: error: GET DATA: Input format DATE5 specifies width 5, but DATE requires a width between 8 and 40. + 32 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /VARIABLES x 1-5 DATE. + | ^~~~~~~~" + +"get-data.sps:33.65-33.77: error: GET DATA: Output format DOLLAR1.2 specifies width 1, but DOLLAR requires a width between 2 and 40. + 33 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /VARIABLES x 1-5 DOLLAR1.2. + | ^~~~~~~~~~~~~" + +"get-data.sps:34.71: error: GET DATA: x is a duplicate variable name. + 34 | GET DATA /TYPE=TXT/FILE='x.txt' /ARRANGEMENT=FIXED /VARIABLES x 1-5 F x 6-10 F. + | ^" + +"get-data.sps:36.21-36.22: error: GET DATA: Syntax error expecting `/CONNECT='. + 36 | GET DATA /TYPE=PSQL **. + | ^~" + +"get-data.sps:37.20-37.23: error: GET DATA: Syntax error expecting `/CONNECT='. + 37 | GET DATA /TYPE=PSQL/ **. + | ^~~~" + +"get-data.sps:38.20-38.30: error: GET DATA: Syntax error expecting `/CONNECT='. + 38 | GET DATA /TYPE=PSQL/CONNECT **. + | ^~~~~~~~~~~" + +"get-data.sps:39.50-39.51: error: GET DATA: Syntax error expecting integer between 1 and 32767 for ASSUMEDSTRWIDTH. + 39 | GET DATA /TYPE=PSQL/CONNECT='db'/ASSUMEDSTRWIDTH=**. + | ^~" + +"get-data.sps:40.40-40.41: error: GET DATA: Syntax error expecting positive integer for BSIZE. + 40 | GET DATA /TYPE=PSQL/CONNECT='db'/BSIZE=**. + | ^~" + +"get-data.sps:41.38-41.39: error: GET DATA: Syntax error expecting string. + 41 | GET DATA /TYPE=PSQL/CONNECT='db'/SQL=**. + | ^~" + +"get-data.sps:43.20-43.21: error: GET DATA: Syntax error expecting `/FILE='. + 43 | GET DATA /TYPE=GNM **. + | ^~" + +"get-data.sps:44.19-44.22: error: GET DATA: Syntax error expecting `/FILE='. + 44 | GET DATA /TYPE=GNM/ **. + | ^~~~" + +"get-data.sps:45.19-45.26: error: GET DATA: Syntax error expecting `/FILE='. + 45 | GET DATA /TYPE=GNM/FILE **. + | ^~~~~~~~" + +"get-data.sps:46.26-46.27: error: GET DATA: Syntax error expecting string. + 46 | GET DATA /TYPE=GNM/FILE= **. + | ^~" + +"get-data.sps:47.54-47.55: error: GET DATA: Syntax error expecting integer between 1 and 32767 for ASSUMEDSTRWIDTH. + 47 | GET DATA /TYPE=GNM/FILE='x.gnumeric'/ASSUMEDSTRWIDTH=**. + | ^~" + +"get-data.sps:48.49-48.50: error: GET DATA: Syntax error expecting string. + 48 | GET DATA /TYPE=GNM/FILE='x.gnumeric'/SHEET=NAME **. + | ^~" + +"get-data.sps:49.50-49.51: error: GET DATA: Syntax error expecting positive integer for INDEX. + 49 | GET DATA /TYPE=GNM/FILE='x.gnumeric'/SHEET=INDEX **. + | ^~" + +"get-data.sps:50.44-50.45: error: GET DATA: Syntax error expecting NAME or INDEX. + 50 | GET DATA /TYPE=GNM/FILE='x.gnumeric'/SHEET=**. + | ^~" + +"get-data.sps:51.54-51.55: error: GET DATA: Syntax error expecting string. + 51 | GET DATA /TYPE=GNM/FILE='x.gnumeric'/CELLRANGE=RANGE **. + | ^~" + +"get-data.sps:52.48-52.49: error: GET DATA: Syntax error expecting FULL or RANGE. + 52 | GET DATA /TYPE=GNM/FILE='x.gnumeric'/CELLRANGE=**. + | ^~" + +"get-data.sps:53.48-53.49: error: GET DATA: Syntax error expecting ON or OFF. + 53 | GET DATA /TYPE=GNM/FILE='x.gnumeric'/READNAMES=**. + | ^~" + +"get-data.sps:54.39-54.40: error: GET DATA: Syntax error expecting ASSUMEDSTRWIDTH, SHEET, CELLRANGE, or READNAMES. + 54 | GET DATA /TYPE=GNM/FILE='x.gnumeric'/ **. + | ^~" +]) +AT_CLEANUP \ No newline at end of file -- 2.30.2