Repository: mathesar-foundation/mathesar

Issue:
Does not work on windows
## Description
This cannot work on Windows because of the pglast dependency. Unfortunately it seems that this is an issue of the libpg_query library and probably will never be fixed: https://github.com/pganalyze/libpg_query/issues/44

## Expected behavior
To be able to install the dependencies on windows

## To Reproduce
Try to instal requirements on windows

## Environment
Windows 10, python 3.9.

## Additional context
If I remove the pglast dependency `pip install -r requirements-dev.txt` works fine. I believe that this can be resolved by removing pglast *completely* since it's used only once. I'd be happy to discuss this more.




Discussion:
The good news is that we're currently moving the logic that necessitated `pglast` down into the database, and so this requirement will be obsolete in the near future.
Hello friends, do we have any news for this issue? I think it would be very useful if we removed that `pglast` dependency so people could run this under windows (I know I want to because I prefer windows for developing, now I need to ssh to a unix machine to run mathesar).

Thank you
Update for you @spapas : We're still working on moving logic down to the database. Unfortunately, the logic requiring the use of `pglast` isn't yet moved out of python. I'll look at what it would take to do that sooner rather than later, since we expect to do it at some point regardless.

A quick inspection of the relevant code makes it look like we'd just need one (admittedly technical) function in the database to make it happen.
Thank you for the update @mathemancer !

Resolving patch:
diff --git a/db/columns/operations/select.py b/db/columns/operations/select.py
--- a/db/columns/operations/select.py
+++ b/db/columns/operations/select.py
@@ -1,21 +1,12 @@
 import warnings
 
-from pglast import Node, parse_sql
 from sqlalchemy import and_, asc, cast, select, text, exists
 
 from db.columns.exceptions import DynamicDefaultWarning
+from db.connection import execute_msar_func_with_engine
 from db.tables.operations.select import reflect_table_from_oid
 from db.utils import execute_statement, get_pg_catalog_table
 
-# These tags define which nodes in the AST built by pglast we consider to be
-# "dynamic" when found in a column default clause.  The nodes are best
-# documented by C header files that define the underlying structs:
-# https://github.com/pganalyze/libpg_query/blob/13-latest/src/postgres/include/nodes/parsenodes.h
-# https://github.com/pganalyze/libpg_query/blob/13-latest/src/postgres/include/nodes/primnodes.h
-# It's possible that more dynamic nodes will be found.  Their tags should be
-# added to this set.
-DYNAMIC_NODE_TAGS = {"SQLValueFunction", "FuncCall"}
-
 
 def get_column_attnum_from_names_as_map(table_oid, column_names, engine, metadata, connection_to_use=None):
     statement = _get_columns_attnum_from_names(table_oid, column_names, engine, metadata=metadata)
@@ -127,10 +118,13 @@ def get_column_default_dict(table_oid, attnum, engine, metadata, connection_to_u
         metadata=metadata,
         connection_to_use=connection_to_use,
     )
+
     if column.server_default is None:
         return
 
-    is_dynamic = _is_default_expr_dynamic(column.server_default)
+    is_dynamic = execute_msar_func_with_engine(
+        engine, 'is_default_possibly_dynamic', table_oid, attnum
+    ).fetchone()[0]
     sql_text = str(column.server_default.arg)
 
     if is_dynamic:
@@ -203,12 +197,3 @@ def _statement_for_triples_of_column_name_and_attnum_and_table_oid(
         conditions.append(attnum_positive)
     sel = sel.where(and_(*conditions))
     return sel
-
-
-def _is_default_expr_dynamic(server_default):
-    prepared_expr = f"""SELECT {server_default.arg.text};"""
-    expr_ast_root = Node(parse_sql(prepared_expr))
-    ast_nodes = {
-        n.node_tag for n in expr_ast_root.traverse() if isinstance(n, Node)
-    }
-    return not ast_nodes.isdisjoint(DYNAMIC_NODE_TAGS)


Test patch:
diff --git a/db/tests/columns/operations/test_select.py b/db/tests/columns/operations/test_select.py
--- a/db/tests/columns/operations/test_select.py
+++ b/db/tests/columns/operations/test_select.py
@@ -1,11 +1,11 @@
 import warnings
 import pytest
 from sqlalchemy import (
-    String, Integer, Column, Table, MetaData, DateTime, func, text, DefaultClause,
+    String, Integer, Column, Table, MetaData, DateTime, func
 )
 from db.columns.exceptions import DynamicDefaultWarning
 from db.columns.operations.select import (
-    get_column_attnum_from_name, get_column_default, _is_default_expr_dynamic,
+    get_column_attnum_from_name, get_column_default,
     get_column_name_from_attnum, get_columns_attnum_from_names,
 )
 from db.tables.operations.select import get_oid_from_table
@@ -118,9 +118,3 @@ def test_get_column_generated_default(engine_with_schema, col):
     ("'abcde'::CHAR(3)", False),
     ("'abcde'::CHAR(5)", False),
 ]
-
-
-@pytest.mark.parametrize("default_expr,is_dynamic", default_expression_test_list)
-def test_is_default_expr_dynamic(default_expr, is_dynamic):
-    default_clause = DefaultClause(text(default_expr))
-    assert _is_default_expr_dynamic(default_clause) is is_dynamic
diff --git a/db/tests/columns/utils.py b/db/tests/columns/utils.py
--- a/db/tests/columns/utils.py
+++ b/db/tests/columns/utils.py
@@ -1,7 +1,7 @@
 import decimal
 
 from sqlalchemy import (
-    CHAR, FLOAT, SMALLINT, String, Integer, BOOLEAN, TEXT, VARCHAR, select, Table, MetaData, NUMERIC, BIGINT, DECIMAL,
+    FLOAT, SMALLINT, String, Integer, BOOLEAN, TEXT, VARCHAR, select, Table, MetaData, NUMERIC, BIGINT, DECIMAL,
     REAL
 )
 from sqlalchemy.dialects.postgresql import DOUBLE_PRECISION, MONEY
@@ -18,7 +18,11 @@
 column_test_dict = {
     BIGINT: {"start": "499999999999", "set": "500000000000", "expt": 500000000000},
     BOOLEAN: {"start": "false", "set": "true", "expt": True},
-    CHAR: {"start": "a", "set": "b", "expt": "b"},
+    # Disabled, since there's a bug in server_default that makes the associated
+    # test buggy. WE are setting the default using `server_default` on the
+    # column, but we *never actually do that in the codebase* (and shouldn't.
+    # It's broken)
+    # CHAR: {"start": "a", "set": "b", "expt": "'b'::bpchar"},
     DECIMAL: {"start": "111.01111", "set": "111.01112", "expt": decimal.Decimal('111.01112')},
     DOUBLE_PRECISION: {"start": "111.01111", "set": "111.01112", "expt": 111.01112},
     DATE: {"start": "1999-01-15 AD", "set": "1999-01-18 AD", "expt": "1999-01-18 AD"},