|
| 1 | +/*- |
| 2 | + * #%L |
| 3 | + * JSQLParser library |
| 4 | + * %% |
| 5 | + * Copyright (C) 2004 - 2026 JSQLParser |
| 6 | + * %% |
| 7 | + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 |
| 8 | + * #L% |
| 9 | + */ |
| 10 | +package net.sf.jsqlparser.statement; |
| 11 | + |
| 12 | +import static org.junit.jupiter.api.Assertions.assertEquals; |
| 13 | +import static org.junit.jupiter.api.Assertions.assertFalse; |
| 14 | +import static org.junit.jupiter.api.Assertions.assertThrows; |
| 15 | +import static org.junit.jupiter.api.Assertions.assertTrue; |
| 16 | + |
| 17 | +import java.util.ArrayList; |
| 18 | +import java.util.List; |
| 19 | +import net.sf.jsqlparser.JSQLParserException; |
| 20 | +import net.sf.jsqlparser.expression.ExpressionVisitorAdapter; |
| 21 | +import net.sf.jsqlparser.expression.LongValue; |
| 22 | +import net.sf.jsqlparser.expression.StringValue; |
| 23 | +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; |
| 24 | +import net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect; |
| 25 | +import net.sf.jsqlparser.parser.CCJSqlParserUtil; |
| 26 | +import net.sf.jsqlparser.statement.SetStatement.AssignmentOperator; |
| 27 | +import net.sf.jsqlparser.util.deparser.SelectDeParser; |
| 28 | +import net.sf.jsqlparser.statement.select.SelectVisitorAdapter; |
| 29 | +import net.sf.jsqlparser.util.deparser.ExpressionDeParser; |
| 30 | +import net.sf.jsqlparser.util.deparser.StatementDeParser; |
| 31 | +import org.junit.jupiter.api.Test; |
| 32 | +import org.junit.jupiter.params.ParameterizedTest; |
| 33 | +import org.junit.jupiter.params.provider.ValueSource; |
| 34 | + |
| 35 | +class PostgreSqlSetToTest { |
| 36 | + private static SetStatement parse(String sql) throws JSQLParserException { |
| 37 | + return (SetStatement) CCJSqlParserUtil.parse(sql, p -> p.withDialect(Dialect.POSTGRESQL)); |
| 38 | + } |
| 39 | + |
| 40 | + @ParameterizedTest |
| 41 | + @ValueSource(strings = {"SET search_path TO my_schema, public", |
| 42 | + "SET LOCAL statement_timeout TO 5000", "SET SESSION application_name TO 'worker'", |
| 43 | + "SET datestyle TO postgres, dmy", "SET client_encoding TO DEFAULT", |
| 44 | + "SET app.user_name TO 'reader'", "SET \"TimeZone\" TO 'UTC'", |
| 45 | + "SET standard_conforming_strings TO on", "SET enable_seqscan TO off", |
| 46 | + "SET enable_hashjoin TO true", "SET enable_mergejoin TO false", |
| 47 | + "SET search_path TO off, on"}) |
| 48 | + void parsesScopeNamesAndValueLists(String sql) throws Exception { |
| 49 | + SetStatement set = parse(sql); |
| 50 | + assertEquals(1, set.getCount()); |
| 51 | + assertEquals(AssignmentOperator.TO, set.getAssignmentOperator()); |
| 52 | + assertFalse(set.isUseEqual()); |
| 53 | + assertRoundTrip(set, sql); |
| 54 | + SetStatement generic = (SetStatement) CCJSqlParserUtil.parse(sql); |
| 55 | + assertEquals(AssignmentOperator.TO, generic.getAssignmentOperator()); |
| 56 | + } |
| 57 | + |
| 58 | + @Test |
| 59 | + void distinguishesOneValueListFromSeveralAssignments() throws Exception { |
| 60 | + SetStatement set = parse("SET LOCAL search_path TO my_schema, public, '$user'"); |
| 61 | + assertEquals("LOCAL", set.getEffectParameter()); |
| 62 | + assertEquals("search_path", set.getName()); |
| 63 | + assertEquals(1, set.getCount()); |
| 64 | + assertEquals(3, set.getExpressions().size()); |
| 65 | + assertEquals("my_schema", set.getExpressions().get(0).toString()); |
| 66 | + assertEquals("public", set.getExpressions().get(1).toString()); |
| 67 | + assertEquals("$user", ((StringValue) set.getExpressions().get(2)).getValue()); |
| 68 | + } |
| 69 | + |
| 70 | + @Test |
| 71 | + void keepsLegacyAssignmentApiAndSeparators() throws Exception { |
| 72 | + SetStatement constructed = new SetStatement("x", new ExpressionList<>(new LongValue(1))); |
| 73 | + assertTrue(constructed.isUseEqual()); |
| 74 | + constructed.setAssignmentOperator(AssignmentOperator.TO); |
| 75 | + assertRoundTrip(constructed, "SET x TO 1"); |
| 76 | + assertFalse(constructed.isUseEqual()); |
| 77 | + constructed.setUseEqual(true); |
| 78 | + assertEquals("SET x = 1", constructed.toString()); |
| 79 | + constructed.setUseEqual(false); |
| 80 | + assertEquals("SET x 1", constructed.toString()); |
| 81 | + SetStatement assignments = (SetStatement) CCJSqlParserUtil.parse("SET @a=1, @b:=2, @c=3", |
| 82 | + p -> p.withDialect(Dialect.MYSQL)); |
| 83 | + assertEquals(3, assignments.getCount()); |
| 84 | + assertEquals(AssignmentOperator.EQUALS, assignments.getAssignmentOperator(0)); |
| 85 | + assertEquals(AssignmentOperator.COLON_EQUALS, assignments.getAssignmentOperator(1)); |
| 86 | + assertEquals("SET @a = 1, @b := 2, @c = 3", assignments.toString()); |
| 87 | + assertRoundTrip(parse("SET TIME ZONE 'UTC'"), "SET Time Zone 'UTC'"); |
| 88 | + } |
| 89 | + |
| 90 | + @Test |
| 91 | + void keepsExistingOnExpressionsOutsideToAssignments() throws Exception { |
| 92 | + for (String sql : List.of("SET v = on + 1", "SET v = 1, on")) { |
| 93 | + for (Dialect dialect : List.of(Dialect.POSTGRESQL, Dialect.MYSQL, Dialect.SQLSERVER)) { |
| 94 | + SetStatement set = (SetStatement) CCJSqlParserUtil.parse(sql, |
| 95 | + p -> p.withDialect(dialect)); |
| 96 | + assertEquals(AssignmentOperator.EQUALS, set.getAssignmentOperator()); |
| 97 | + assertRoundTrip(set, sql); |
| 98 | + } |
| 99 | + } |
| 100 | + } |
| 101 | + |
| 102 | + @Test |
| 103 | + void visitsValuesInToEqualsAndSpaceForms() throws Exception { |
| 104 | + List<Long> values = new ArrayList<>(); |
| 105 | + StatementVisitorAdapter<Void> visitor = new StatementVisitorAdapter<>( |
| 106 | + new SelectVisitorAdapter<>(new ExpressionVisitorAdapter<Void>() { |
| 107 | + @Override |
| 108 | + public <S> Void visit(LongValue value, S context) { |
| 109 | + values.add(value.getValue()); |
| 110 | + return null; |
| 111 | + } |
| 112 | + })); |
| 113 | + parse("SET x TO 1, 2").accept(visitor, null); |
| 114 | + CCJSqlParserUtil.parse("SET x = 3, y 4").accept(visitor, null); |
| 115 | + assertEquals(List.of(1L, 2L, 3L, 4L), values); |
| 116 | + StringBuilder output = new StringBuilder(); |
| 117 | + ExpressionDeParser expressions = new ExpressionDeParser() { |
| 118 | + @Override |
| 119 | + public <S> StringBuilder visit(LongValue value, S context) { |
| 120 | + return getBuilder().append(value.getValue() + 10); |
| 121 | + } |
| 122 | + }; |
| 123 | + parse("SET x TO 1, 2").accept( |
| 124 | + new StatementDeParser(expressions, new SelectDeParser(), output), null); |
| 125 | + assertEquals("SET x TO 11, 12", output.toString()); |
| 126 | + } |
| 127 | + |
| 128 | + @Test |
| 129 | + void retainsFollowingStatements() throws Exception { |
| 130 | + Statements statements = CCJSqlParserUtil.parseStatements( |
| 131 | + "SET LOCAL search_path TO my_schema, public; SELECT 1; SET x = 3;", |
| 132 | + p -> p.withDialect(Dialect.POSTGRESQL)); |
| 133 | + assertEquals(3, statements.size()); |
| 134 | + assertEquals(AssignmentOperator.TO, |
| 135 | + ((SetStatement) statements.get(0)).getAssignmentOperator()); |
| 136 | + assertEquals(AssignmentOperator.EQUALS, |
| 137 | + ((SetStatement) statements.get(2)).getAssignmentOperator()); |
| 138 | + } |
| 139 | + |
| 140 | + @ParameterizedTest |
| 141 | + @ValueSource(strings = {"SET search_path TO", "SET search_path TO public,", |
| 142 | + "SET x TO = 1", "SET x TO 1, y TO 2", "SET x = 1, y TO 2", |
| 143 | + "SET @x TO 1", "SET TIME ZONE TO 'UTC'"}) |
| 144 | + void rejectsMalformedOrMultipleParameterToAssignments(String sql) { |
| 145 | + assertThrows(JSQLParserException.class, () -> parse(sql)); |
| 146 | + } |
| 147 | + |
| 148 | + @Test |
| 149 | + void doesNotEnableToForExplicitMySqlDialect() { |
| 150 | + assertThrows(JSQLParserException.class, () -> CCJSqlParserUtil.parse("SET x TO 1", |
| 151 | + p -> p.withDialect(Dialect.MYSQL))); |
| 152 | + } |
| 153 | + |
| 154 | + private static void assertRoundTrip(SetStatement set, String expected) throws Exception { |
| 155 | + assertEquals(expected, set.toString()); |
| 156 | + StringBuilder output = new StringBuilder(); |
| 157 | + set.accept(new StatementDeParser(output), null); |
| 158 | + assertEquals(expected, output.toString()); |
| 159 | + SetStatement reparsed = parse(output.toString()); |
| 160 | + assertEquals(set.getAssignmentOperator(), reparsed.getAssignmentOperator()); |
| 161 | + assertEquals(set.getEffectParameter(), reparsed.getEffectParameter()); |
| 162 | + assertEquals(set.getName(), reparsed.getName()); |
| 163 | + assertEquals(set.getExpressions().toString(), reparsed.getExpressions().toString()); |
| 164 | + } |
| 165 | +} |
0 commit comments