Repository: tobymao/sqlglot

Issue:
DuckDB bracket operator is 1-indexed
**Before you file an issue**
- Make sure you specify the "read" dialect eg. parse_one(sql, read="spark") Bigquery, for example
- Check if the issue still exists on main yes

**Fully reproducible code snippet**
Please include a fully reproducible code snippet or the input sql, dialect, and expected output.

DuckDB arrays are 1-indexed. When providing a literal to the subscript/bracket operator the expected output for a transpilation of
```sql
SELECT some_arr[0] AS first FROM blah
```
should render to
```sql
SELECT some_arr[1] AS first FROM blah
```
in DuckDB. However, this is not the case:
```python
>>> transpile("SELECT some_arr[0] AS first FROM blah", read="bigquery", write="duckdb")
['SELECT some_arr[0] AS first FROM blah']
```

I attempted to patch this by incrementing in the case that `exp.Bracket.expressions` is of length 1 and type `Literal`, but this does not seem to be directionally correct (do you still increment in the case where the subscript is a column?). I think there are some semantics concerns here which I'm not well-versed enough to address.

**Official Documentation**
Please include links to official SQL documentation related to your issue.

[DuckDB array documentation](https://duckdb.org/docs/sql/data_types/array.html)


Discussion:

Patch:
diff --git a/sqlglot/dialects/duckdb.py b/sqlglot/dialects/duckdb.py
index 44f917f0..137cd367 100644
--- a/sqlglot/dialects/duckdb.py
+++ b/sqlglot/dialects/duckdb.py
@@ -127,6 +127,7 @@ class DuckDB(Dialect):
     NULL_ORDERING = "nulls_are_last"
     SUPPORTS_USER_DEFINED_TYPES = False
     SAFE_DIVISION = True
+    INDEX_OFFSET = 1
 
     # https://duckdb.org/docs/sql/introduction.html#creating-a-new-table
     RESOLVES_IDENTIFIERS_AS_UPPERCASE = None


Test patch:
diff --git a/tests/dialects/test_duckdb.py b/tests/dialects/test_duckdb.py
index b053f355..687a807b 100644
--- a/tests/dialects/test_duckdb.py
+++ b/tests/dialects/test_duckdb.py
@@ -1,4 +1,5 @@
 from sqlglot import ErrorLevel, UnsupportedError, exp, parse_one, transpile
+from sqlglot.helper import logger as helper_logger
 from tests.dialects.test_dialect import Validator
 
 
@@ -96,7 +97,6 @@ class TestDuckDB(Validator):
         )
 
         self.validate_identity("SELECT i FROM RANGE(5) AS _(i) ORDER BY i ASC")
-        self.validate_identity("[x.STRING_SPLIT(' ')[1] FOR x IN ['1', '2', 3] IF x.CONTAINS('1')]")
         self.validate_identity("INSERT INTO x BY NAME SELECT 1 AS y")
         self.validate_identity("SELECT 1 AS x UNION ALL BY NAME SELECT 2 AS x")
         self.validate_identity("SELECT SUM(x) FILTER (x = 1)", "SELECT SUM(x) FILTER(WHERE x = 1)")
@@ -504,6 +504,35 @@ class TestDuckDB(Validator):
 
         self.validate_identity("SELECT ISNAN(x)")
 
+    def test_array_index(self):
+        with self.assertLogs(helper_logger) as cm:
+            self.validate_all(
+                "SELECT some_arr[1] AS first FROM blah",
+                read={
+                    "bigquery": "SELECT some_arr[0] AS first FROM blah",
+                },
+                write={
+                    "bigquery": "SELECT some_arr[0] AS first FROM blah",
+                    "duckdb": "SELECT some_arr[1] AS first FROM blah",
+                    "presto": "SELECT some_arr[1] AS first FROM blah",
+                },
+            )
+            self.validate_identity(
+                "[x.STRING_SPLIT(' ')[1] FOR x IN ['1', '2', 3] IF x.CONTAINS('1')]"
+            )
+
+            self.assertEqual(
+                cm.output,
+                [
+                    "WARNING:sqlglot:Applying array index offset (-1)",
+                    "WARNING:sqlglot:Applying array index offset (1)",
+                    "WARNING:sqlglot:Applying array index offset (1)",
+                    "WARNING:sqlglot:Applying array index offset (1)",
+                    "WARNING:sqlglot:Applying array index offset (-1)",
+                    "WARNING:sqlglot:Applying array index offset (1)",
+                ],
+            )
+
     def test_time(self):
         self.validate_identity("SELECT CURRENT_DATE")
         self.validate_identity("SELECT CURRENT_TIMESTAMP")