fakesnow 0.9.28__py3-none-any.whl → 0.9.29__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
fakesnow/conn.py CHANGED
@@ -42,13 +42,15 @@ class FakeSnowflakeConnection:
42
42
  # information_schema.schemata below we use upper-case to match any existing duckdb
43
43
  # catalog or schemas like "information_schema"
44
44
  self.database = database and database.upper()
45
- self.schema = schema and schema.upper()
45
+ self._schema = schema and (
46
+ "_FS_INFORMATION_SCHEMA" if schema.upper() == "INFORMATION_SCHEMA" else schema.upper()
47
+ )
46
48
 
47
49
  self.database_set = False
48
50
  self.schema_set = False
49
51
  self.db_path = Path(db_path) if db_path else None
50
52
  self.nop_regexes = nop_regexes
51
- self._paramstyle = snowflake.connector.paramstyle
53
+ self._paramstyle = kwargs.get("paramstyle", snowflake.connector.paramstyle)
52
54
  self.variables = Variables()
53
55
 
54
56
  # create database if needed
@@ -69,24 +71,24 @@ class FakeSnowflakeConnection:
69
71
  if (
70
72
  create_schema
71
73
  and self.database
72
- and self.schema
74
+ and self._schema
73
75
  and not duck_conn.execute(
74
76
  f"""select * from information_schema.schemata
75
- where upper(catalog_name) = '{self.database}' and upper(schema_name) = '{self.schema}'"""
77
+ where upper(catalog_name) = '{self.database}' and upper(schema_name) = '{self._schema}'"""
76
78
  ).fetchone()
77
79
  ):
78
- duck_conn.execute(f"CREATE SCHEMA {self.database}.{self.schema}")
80
+ duck_conn.execute(f"CREATE SCHEMA {self.database}.{self._schema}")
79
81
 
80
82
  # set database and schema if both exist
81
83
  if (
82
84
  self.database
83
- and self.schema
85
+ and self._schema
84
86
  and duck_conn.execute(
85
87
  f"""select * from information_schema.schemata
86
- where upper(catalog_name) = '{self.database}' and upper(schema_name) = '{self.schema}'"""
88
+ where upper(catalog_name) = '{self.database}' and upper(schema_name) = '{self._schema}'"""
87
89
  ).fetchone()
88
90
  ):
89
- duck_conn.execute(f"SET schema='{self.database}.{self.schema}'")
91
+ duck_conn.execute(f"SET schema='{self.database}.{self._schema}'")
90
92
  self.database_set = True
91
93
  self.schema_set = True
92
94
  # set database if only that exists
@@ -149,3 +151,7 @@ class FakeSnowflakeConnection:
149
151
 
150
152
  def rollback(self) -> None:
151
153
  self.cursor().execute("ROLLBACK")
154
+
155
+ @property
156
+ def schema(self) -> str | None:
157
+ return "INFORMATION_SCHEMA" if self._schema == "_FS_INFORMATION_SCHEMA" else self._schema
fakesnow/cursor.py CHANGED
@@ -145,6 +145,8 @@ class FakeSnowflakeCursor:
145
145
  return self
146
146
 
147
147
  expression = parse_one(command, read="snowflake")
148
+ self.check_db_and_schema(expression)
149
+
148
150
  for exp in self._transform_explode(expression):
149
151
  transformed = self._transform(exp)
150
152
  self._execute(transformed, params)
@@ -154,6 +156,24 @@ class FakeSnowflakeCursor:
154
156
  self._sqlstate = e.sqlstate
155
157
  raise e
156
158
 
159
+ def check_db_and_schema(self, expression: exp.Expression) -> None:
160
+ no_database, no_schema = checks.is_unqualified_table_expression(expression)
161
+
162
+ if no_database and not self._conn.database_set:
163
+ cmd = expr.key_command(expression)
164
+ raise snowflake.connector.errors.ProgrammingError(
165
+ msg=f"Cannot perform {cmd}. This session does not have a current database. Call 'USE DATABASE', or use a qualified name.", # noqa: E501
166
+ errno=90105,
167
+ sqlstate="22000",
168
+ )
169
+ elif no_schema and not self._conn.schema_set:
170
+ cmd = expr.key_command(expression)
171
+ raise snowflake.connector.errors.ProgrammingError(
172
+ msg=f"Cannot perform {cmd}. This session does not have a current schema. Call 'USE SCHEMA', or use a qualified name.", # noqa: E501
173
+ errno=90106,
174
+ sqlstate="22000",
175
+ )
176
+
157
177
  def _transform(self, expression: exp.Expression) -> exp.Expression:
158
178
  return (
159
179
  expression.transform(transforms.upper_case_unquoted_identifiers)
@@ -163,7 +183,8 @@ class FakeSnowflakeCursor:
163
183
  .transform(transforms.extract_comment_on_table)
164
184
  .transform(transforms.extract_comment_on_columns)
165
185
  .transform(transforms.information_schema_fs_columns_snowflake)
166
- .transform(transforms.information_schema_fs_tables_ext)
186
+ .transform(transforms.information_schema_databases, current_schema=self._conn.schema)
187
+ .transform(transforms.information_schema_fs_tables)
167
188
  .transform(transforms.information_schema_fs_views)
168
189
  .transform(transforms.drop_schema_cascade)
169
190
  .transform(transforms.tag)
@@ -228,21 +249,6 @@ class FakeSnowflakeCursor:
228
249
 
229
250
  cmd = expr.key_command(transformed)
230
251
 
231
- no_database, no_schema = checks.is_unqualified_table_expression(transformed)
232
-
233
- if no_database and not self._conn.database_set:
234
- raise snowflake.connector.errors.ProgrammingError(
235
- msg=f"Cannot perform {cmd}. This session does not have a current database. Call 'USE DATABASE', or use a qualified name.", # noqa: E501
236
- errno=90105,
237
- sqlstate="22000",
238
- )
239
- elif no_schema and not self._conn.schema_set:
240
- raise snowflake.connector.errors.ProgrammingError(
241
- msg=f"Cannot perform {cmd}. This session does not have a current schema. Call 'USE SCHEMA', or use a qualified name.", # noqa: E501
242
- errno=90106,
243
- sqlstate="22000",
244
- )
245
-
246
252
  sql = transformed.sql(dialect="duckdb")
247
253
 
248
254
  if transformed.find(exp.Select) and (seed := transformed.args.get("seed")):
@@ -278,7 +284,7 @@ class FakeSnowflakeCursor:
278
284
  self._conn.database_set = True
279
285
 
280
286
  elif set_schema := transformed.args.get("set_schema"):
281
- self._conn.schema = set_schema
287
+ self._conn._schema = set_schema # noqa: SLF001
282
288
  self._conn.schema_set = True
283
289
 
284
290
  elif create_db_name := transformed.args.get("create_db_name"):
@@ -328,10 +334,10 @@ class FakeSnowflakeCursor:
328
334
  # if dropping the current database/schema then reset conn metadata
329
335
  if cmd == "DROP DATABASE" and ident == self._conn.database:
330
336
  self._conn.database = None
331
- self._conn.schema = None
337
+ self._conn._schema = None # noqa: SLF001
332
338
 
333
339
  elif cmd == "DROP SCHEMA" and ident == self._conn.schema:
334
- self._conn.schema = None
340
+ self._conn._schema = None # noqa: SLF001
335
341
 
336
342
  if table_comment := cast(tuple[exp.Table, str], transformed.args.get("table_comment")):
337
343
  # record table comment
fakesnow/info_schema.py CHANGED
@@ -4,10 +4,11 @@ from __future__ import annotations
4
4
 
5
5
  from string import Template
6
6
 
7
+ from fakesnow.instance import GLOBAL_DATABASE_NAME
8
+
7
9
  # use ext prefix in columns to disambiguate when joining with information_schema.tables
8
- SQL_CREATE_INFORMATION_SCHEMA_TABLES_EXT = Template(
9
- """
10
- create table if not exists ${catalog}.information_schema._fs_tables_ext (
10
+ SQL_CREATE_INFORMATION_SCHEMA_TABLES_EXT = f"""
11
+ create table if not exists {GLOBAL_DATABASE_NAME}.main._fs_tables_ext (
11
12
  ext_table_catalog varchar,
12
13
  ext_table_schema varchar,
13
14
  ext_table_name varchar,
@@ -15,11 +16,10 @@ create table if not exists ${catalog}.information_schema._fs_tables_ext (
15
16
  PRIMARY KEY(ext_table_catalog, ext_table_schema, ext_table_name)
16
17
  )
17
18
  """
18
- )
19
19
 
20
- SQL_CREATE_INFORMATION_SCHEMA_COLUMNS_EXT = Template(
21
- """
22
- create table if not exists ${catalog}.information_schema._fs_columns_ext (
20
+
21
+ SQL_CREATE_INFORMATION_SCHEMA_COLUMNS_EXT = f"""
22
+ create table if not exists {GLOBAL_DATABASE_NAME}.main._fs_columns_ext (
23
23
  ext_table_catalog varchar,
24
24
  ext_table_schema varchar,
25
25
  ext_table_name varchar,
@@ -29,13 +29,19 @@ create table if not exists ${catalog}.information_schema._fs_columns_ext (
29
29
  PRIMARY KEY(ext_table_catalog, ext_table_schema, ext_table_name, ext_column_name)
30
30
  )
31
31
  """
32
+
33
+ SQL_CREATE_FS_INFORMATION_SCHEMA = Template(
34
+ """
35
+ create schema if not exists ${catalog}._fs_information_schema
36
+ """
32
37
  )
33
38
 
39
+
34
40
  # only include fields applicable to snowflake (as mentioned by describe table information_schema.columns)
35
41
  # snowflake integers are 38 digits, base 10, See https://docs.snowflake.com/en/sql-reference/data-types-numeric
36
42
  SQL_CREATE_INFORMATION_SCHEMA_COLUMNS_VIEW = Template(
37
43
  """
38
- create view if not exists ${catalog}.information_schema._fs_columns_snowflake AS
44
+ create view if not exists ${catalog}._fs_information_schema._fs_columns_snowflake AS
39
45
  select
40
46
  columns.table_catalog AS table_catalog,
41
47
  columns.table_schema AS table_schema,
@@ -64,8 +70,8 @@ collation_name, is_identity, identity_generation, identity_cycle,
64
70
  ddb_columns.comment as comment,
65
71
  null::VARCHAR as identity_start,
66
72
  null::VARCHAR as identity_increment,
67
- from ${catalog}.information_schema.columns columns
68
- left join ${catalog}.information_schema._fs_columns_ext ext
73
+ from system.information_schema.columns columns
74
+ left join _fs_global.main._fs_columns_ext ext
69
75
  on ext_table_catalog = columns.table_catalog
70
76
  AND ext_table_schema = columns.table_schema
71
77
  AND ext_table_name = columns.table_name
@@ -75,6 +81,8 @@ LEFT JOIN duckdb_columns ddb_columns
75
81
  AND ddb_columns.schema_name = columns.table_schema
76
82
  AND ddb_columns.table_name = columns.table_name
77
83
  AND ddb_columns.column_name = columns.column_name
84
+ where database_name = '${catalog}'
85
+ and schema_name != '_fs_information_schema'
78
86
  """
79
87
  )
80
88
 
@@ -82,7 +90,7 @@ LEFT JOIN duckdb_columns ddb_columns
82
90
  # replicates https://docs.snowflake.com/sql-reference/info-schema/databases
83
91
  SQL_CREATE_INFORMATION_SCHEMA_DATABASES_VIEW = Template(
84
92
  """
85
- create view if not exists ${catalog}.information_schema.databases AS
93
+ create view if not exists ${catalog}._fs_information_schema.databases AS
86
94
  select
87
95
  catalog_name as database_name,
88
96
  'SYSADMIN' as database_owner,
@@ -92,17 +100,31 @@ select
92
100
  to_timestamp(0)::timestamptz as last_altered,
93
101
  1 as retention_time,
94
102
  'STANDARD' as type
95
- from information_schema.schemata
103
+ from system.information_schema.schemata
96
104
  where catalog_name not in ('memory', 'system', 'temp', '_fs_global')
97
- and schema_name = 'information_schema'
105
+ and schema_name = 'main'
98
106
  """
99
107
  )
100
108
 
109
+ # replicates https://docs.snowflake.com/sql-reference/info-schema/tables
110
+ SQL_CREATE_INFORMATION_SCHEMA_TABLES_VIEW = Template(
111
+ """
112
+ create view if not exists ${catalog}._fs_information_schema._fs_tables AS
113
+ select *
114
+ from system.information_schema.tables tables
115
+ left join _fs_global.main._fs_tables_ext on
116
+ tables.table_catalog = _fs_tables_ext.ext_table_catalog AND
117
+ tables.table_schema = _fs_tables_ext.ext_table_schema AND
118
+ tables.table_name = _fs_tables_ext.ext_table_name
119
+ where table_catalog = '${catalog}'
120
+ and table_schema != '_fs_information_schema'
121
+ """
122
+ )
101
123
 
102
124
  # replicates https://docs.snowflake.com/sql-reference/info-schema/views
103
125
  SQL_CREATE_INFORMATION_SCHEMA_VIEWS_VIEW = Template(
104
126
  """
105
- create view if not exists ${catalog}.information_schema._fs_views AS
127
+ create view if not exists ${catalog}._fs_information_schema._fs_views AS
106
128
  select
107
129
  database_name as table_catalog,
108
130
  schema_name as table_schema,
@@ -120,24 +142,31 @@ select
120
142
  null::VARCHAR as comment
121
143
  from duckdb_views
122
144
  where database_name = '${catalog}'
123
- and schema_name != 'information_schema'
145
+ and schema_name != '_fs_information_schema'
124
146
  """
125
147
  )
126
148
 
127
149
 
128
150
  def creation_sql(catalog: str) -> str:
129
151
  return f"""
130
- {SQL_CREATE_INFORMATION_SCHEMA_TABLES_EXT.substitute(catalog=catalog)};
131
- {SQL_CREATE_INFORMATION_SCHEMA_COLUMNS_EXT.substitute(catalog=catalog)};
152
+ {SQL_CREATE_FS_INFORMATION_SCHEMA.substitute(catalog=catalog)};
132
153
  {SQL_CREATE_INFORMATION_SCHEMA_COLUMNS_VIEW.substitute(catalog=catalog)};
133
154
  {SQL_CREATE_INFORMATION_SCHEMA_DATABASES_VIEW.substitute(catalog=catalog)};
155
+ {SQL_CREATE_INFORMATION_SCHEMA_TABLES_VIEW.substitute(catalog=catalog)};
134
156
  {SQL_CREATE_INFORMATION_SCHEMA_VIEWS_VIEW.substitute(catalog=catalog)};
135
157
  """
136
158
 
137
159
 
160
+ def fs_global_creation_sql(catalog: str) -> str:
161
+ return f"""
162
+ {SQL_CREATE_INFORMATION_SCHEMA_TABLES_EXT};
163
+ {SQL_CREATE_INFORMATION_SCHEMA_COLUMNS_EXT};
164
+ """
165
+
166
+
138
167
  def insert_table_comment_sql(catalog: str, schema: str, table: str, comment: str) -> str:
139
168
  return f"""
140
- INSERT INTO {catalog}.information_schema._fs_tables_ext
169
+ INSERT INTO {GLOBAL_DATABASE_NAME}.main._fs_tables_ext
141
170
  values ('{catalog}', '{schema}', '{table}', '{comment}')
142
171
  ON CONFLICT (ext_table_catalog, ext_table_schema, ext_table_name)
143
172
  DO UPDATE SET comment = excluded.comment
@@ -151,7 +180,7 @@ def insert_text_lengths_sql(catalog: str, schema: str, table: str, text_lengths:
151
180
  )
152
181
 
153
182
  return f"""
154
- INSERT INTO {catalog}.information_schema._fs_columns_ext
183
+ INSERT INTO {GLOBAL_DATABASE_NAME}.main._fs_columns_ext
155
184
  values {values}
156
185
  ON CONFLICT (ext_table_catalog, ext_table_schema, ext_table_name, ext_column_name)
157
186
  DO UPDATE SET ext_character_maximum_length = excluded.ext_character_maximum_length,
fakesnow/instance.py CHANGED
@@ -6,6 +6,7 @@ from typing import Any
6
6
  import duckdb
7
7
 
8
8
  import fakesnow.fakes as fakes
9
+ from fakesnow import info_schema
9
10
 
10
11
  GLOBAL_DATABASE_NAME = "_fs_global"
11
12
  USERS_TABLE_FQ_NAME = f"{GLOBAL_DATABASE_NAME}._fs_users_ext"
@@ -71,6 +72,8 @@ class FakeSnow:
71
72
  # create a "global" database for storing objects which span databases.
72
73
  self.duck_conn.execute(f"ATTACH IF NOT EXISTS ':memory:' AS {GLOBAL_DATABASE_NAME}")
73
74
  self.duck_conn.execute(SQL_CREATE_INFORMATION_SCHEMA_USERS_TABLE_EXT)
75
+ # create the info schema extensions
76
+ self.duck_conn.execute(info_schema.fs_global_creation_sql(GLOBAL_DATABASE_NAME))
74
77
 
75
78
  def connect(
76
79
  self, database: str | None = None, schema: str | None = None, **kwargs: Any
fakesnow/server.py CHANGED
@@ -34,7 +34,9 @@ async def login_request(request: Request) -> JSONResponse:
34
34
  database = request.query_params.get("databaseName")
35
35
  schema = request.query_params.get("schemaName")
36
36
  body = await request.body()
37
- body_json = json.loads(gzip.decompress(body))
37
+ if request.headers.get("Content-Encoding") == "gzip":
38
+ body = gzip.decompress(body)
39
+ body_json = json.loads(body)
38
40
  session_params: dict[str, Any] = body_json["data"]["SESSION_PARAMETERS"]
39
41
  if db_path := session_params.get("FAKESNOW_DB_PATH"):
40
42
  # isolated creates a new in-memory database, rather than using the shared in-memory database
@@ -53,7 +55,10 @@ async def query_request(request: Request) -> JSONResponse:
53
55
  conn = to_conn(request)
54
56
 
55
57
  body = await request.body()
56
- body_json = json.loads(gzip.decompress(body))
58
+ if request.headers.get("Content-Encoding") == "gzip":
59
+ body = gzip.decompress(body)
60
+
61
+ body_json = json.loads(body)
57
62
 
58
63
  sql_text = body_json["sqlText"]
59
64
 
fakesnow/transforms.py CHANGED
@@ -11,7 +11,6 @@ from fakesnow import transforms_merge
11
11
  from fakesnow.instance import USERS_TABLE_FQ_NAME
12
12
  from fakesnow.variables import Variables
13
13
 
14
- MISSING_DATABASE = "missing_database"
15
14
  SUCCESS_NOP = sqlglot.parse_one("SELECT 'Statement executed successfully.' as status")
16
15
 
17
16
 
@@ -167,7 +166,7 @@ SELECT
167
166
  NULL::VARCHAR AS "comment",
168
167
  NULL::VARCHAR AS "policy name",
169
168
  NULL::JSON AS "privacy domain",
170
- FROM information_schema._fs_columns_snowflake
169
+ FROM _fs_information_schema._fs_columns_snowflake
171
170
  WHERE table_catalog = '${catalog}' AND table_schema = '${schema}' AND table_name = '${table}'
172
171
  ORDER BY ordinal_position
173
172
  """
@@ -188,7 +187,7 @@ SELECT
188
187
  NULL::VARCHAR AS "comment",
189
188
  NULL::VARCHAR AS "policy name",
190
189
  NULL::JSON AS "privacy domain",
191
- FROM (DESCRIBE information_schema.${view})
190
+ FROM (DESCRIBE ${view})
192
191
  """
193
192
  )
194
193
 
@@ -211,9 +210,17 @@ def describe_table(
211
210
  catalog = table.catalog or current_database
212
211
  schema = table.db or current_schema
213
212
 
213
+ # TODO - move this after information_schema_fs_columns_snowflake
214
214
  if schema and schema.upper() == "INFORMATION_SCHEMA":
215
215
  # information schema views don't exist in _fs_columns_snowflake
216
- return sqlglot.parse_one(SQL_DESCRIBE_INFO_SCHEMA.substitute(view=table.name), read="duckdb")
216
+ return sqlglot.parse_one(
217
+ SQL_DESCRIBE_INFO_SCHEMA.substitute(view=f"system.information_schema.{table.name}"), read="duckdb"
218
+ )
219
+ elif table.name.upper() == "_FS_COLUMNS_SNOWFLAKE":
220
+ # information schema views don't exist in _fs_columns_snowflake
221
+ return sqlglot.parse_one(
222
+ SQL_DESCRIBE_INFO_SCHEMA.substitute(view="_fs_information_schema._FS_COLUMNS_SNOWFLAKE"), read="duckdb"
223
+ )
217
224
 
218
225
  return sqlglot.parse_one(
219
226
  SQL_DESCRIBE_TABLE.substitute(catalog=catalog, schema=schema, table=table.name),
@@ -596,7 +603,7 @@ def indices_to_json_extract(expression: exp.Expression) -> exp.Expression:
596
603
 
597
604
 
598
605
  def information_schema_fs_columns_snowflake(expression: exp.Expression) -> exp.Expression:
599
- """Redirect to the information_schema._fs_columns_snowflake view which has metadata that matches snowflake.
606
+ """Redirect to the _FS_COLUMNS_SNOWFLAKE view which has metadata that matches snowflake.
600
607
 
601
608
  Because duckdb doesn't store character_maximum_length or character_octet_length.
602
609
  """
@@ -609,44 +616,58 @@ def information_schema_fs_columns_snowflake(expression: exp.Expression) -> exp.E
609
616
  and expression.name.upper() == "COLUMNS"
610
617
  ):
611
618
  expression.set("this", exp.Identifier(this="_FS_COLUMNS_SNOWFLAKE", quoted=False))
619
+ expression.set("db", exp.Identifier(this="_FS_INFORMATION_SCHEMA", quoted=False))
612
620
 
613
621
  return expression
614
622
 
615
623
 
616
- def information_schema_fs_tables_ext(expression: exp.Expression) -> exp.Expression:
617
- """Join to information_schema._fs_tables_ext to access additional metadata columns (eg: comment)."""
624
+ def information_schema_databases(
625
+ expression: exp.Expression,
626
+ current_schema: str | None = None,
627
+ ) -> exp.Expression:
628
+ if (
629
+ isinstance(expression, exp.Table)
630
+ and (
631
+ expression.db.upper() == "INFORMATION_SCHEMA"
632
+ or (current_schema and current_schema.upper() == "INFORMATION_SCHEMA")
633
+ )
634
+ and expression.name.upper() == "DATABASES"
635
+ ):
636
+ return exp.Table(
637
+ this=exp.Identifier(this="DATABASES", quoted=False),
638
+ db=exp.Identifier(this="_FS_INFORMATION_SCHEMA", quoted=False),
639
+ )
640
+ return expression
641
+
642
+
643
+ def information_schema_fs_tables(
644
+ expression: exp.Expression,
645
+ ) -> exp.Expression:
646
+ """Use _FS_TABLES to access additional metadata columns (eg: comment)."""
618
647
 
619
648
  if (
620
649
  isinstance(expression, exp.Select)
621
- and (tbl_exp := expression.find(exp.Table))
622
- and tbl_exp.name.upper() == "TABLES"
623
- and tbl_exp.db.upper() == "INFORMATION_SCHEMA"
650
+ and (tbl := expression.find(exp.Table))
651
+ and tbl.db.upper() == "INFORMATION_SCHEMA"
652
+ and tbl.name.upper() == "TABLES"
624
653
  ):
625
- return expression.join(
626
- "information_schema._fs_tables_ext",
627
- on=(
628
- """
629
- tables.table_catalog = _fs_tables_ext.ext_table_catalog AND
630
- tables.table_schema = _fs_tables_ext.ext_table_schema AND
631
- tables.table_name = _fs_tables_ext.ext_table_name
632
- """
633
- ),
634
- join_type="left",
635
- )
654
+ tbl.set("this", exp.Identifier(this="_FS_TABLES", quoted=False))
655
+ tbl.set("db", exp.Identifier(this="_FS_INFORMATION_SCHEMA", quoted=False))
636
656
 
637
657
  return expression
638
658
 
639
659
 
640
660
  def information_schema_fs_views(expression: exp.Expression) -> exp.Expression:
641
- """Use information_schema._fs_views to return Snowflake's version instead of duckdb's."""
661
+ """Use _FS_VIEWS to return Snowflake's version instead of duckdb's."""
642
662
 
643
663
  if (
644
664
  isinstance(expression, exp.Select)
645
- and (tbl_exp := expression.find(exp.Table))
646
- and tbl_exp.name.upper() == "VIEWS"
647
- and tbl_exp.db.upper() == "INFORMATION_SCHEMA"
665
+ and (tbl := expression.find(exp.Table))
666
+ and tbl.db.upper() == "INFORMATION_SCHEMA"
667
+ and tbl.name.upper() == "VIEWS"
648
668
  ):
649
- tbl_exp.set("this", exp.Identifier(this="_FS_VIEWS", quoted=False))
669
+ tbl.set("this", exp.Identifier(this="_FS_VIEWS", quoted=False))
670
+ tbl.set("db", exp.Identifier(this="_FS_INFORMATION_SCHEMA", quoted=False))
650
671
 
651
672
  return expression
652
673
 
@@ -921,7 +942,10 @@ def set_schema(expression: exp.Expression, current_database: str | None) -> exp.
921
942
  db_name = db.name
922
943
  else:
923
944
  # isn't qualified with a database
924
- db_name = current_database or MISSING_DATABASE
945
+ db_name = current_database
946
+
947
+ # assertion always true because check_db_schema is called before this
948
+ assert db_name
925
949
 
926
950
  schema = expression.this.name
927
951
  return exp.Command(
@@ -960,7 +984,7 @@ def show_objects_tables(expression: exp.Expression, current_database: str | None
960
984
  schema = None
961
985
 
962
986
  tables_only = "table_type = 'BASE TABLE' and " if show == "TABLES" else ""
963
- exclude_fakesnow_tables = "not (table_schema == 'information_schema' and table_name like '_fs_%%')"
987
+ exclude_fakesnow_tables = "not (table_schema == '_fs_information_schema')"
964
988
  # without a database will show everything in the "account"
965
989
  table_catalog = f" and table_catalog = '{catalog}'" if catalog else ""
966
990
  schema = f" and table_schema = '{schema}'" if schema else ""
@@ -991,12 +1015,16 @@ def show_objects_tables(expression: exp.Expression, current_database: str | None
991
1015
  SQL_SHOW_SCHEMAS = """
992
1016
  select
993
1017
  to_timestamp(0)::timestamptz as 'created_on',
994
- schema_name as 'name',
1018
+ case
1019
+ when schema_name = '_fs_information_schema' then 'information_schema'
1020
+ else schema_name
1021
+ end as 'name',
995
1022
  NULL as 'kind',
996
1023
  catalog_name as 'database_name',
997
1024
  NULL as 'schema_name'
998
1025
  from information_schema.schemata
999
- where catalog_name not in ('memory', 'system', 'temp') and schema_name not in ('main', 'pg_catalog')
1026
+ where not catalog_name in ('memory', 'system', 'temp')
1027
+ and not schema_name in ('main', 'pg_catalog')
1000
1028
  """
1001
1029
 
1002
1030
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: fakesnow
3
- Version: 0.9.28
3
+ Version: 0.9.29
4
4
  Summary: Fake Snowflake Connector for Python. Run, mock and test Snowflake DB locally.
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -210,10 +210,10 @@ Classifier: License :: OSI Approved :: MIT License
210
210
  Requires-Python: >=3.9
211
211
  Description-Content-Type: text/markdown
212
212
  License-File: LICENSE
213
- Requires-Dist: duckdb~=1.1.3
213
+ Requires-Dist: duckdb~=1.2.0
214
214
  Requires-Dist: pyarrow
215
215
  Requires-Dist: snowflake-connector-python
216
- Requires-Dist: sqlglot~=26.3.9
216
+ Requires-Dist: sqlglot~=26.6.0
217
217
  Provides-Extra: dev
218
218
  Requires-Dist: build~=1.0; extra == "dev"
219
219
  Requires-Dist: dirty-equals; extra == "dev"
@@ -3,24 +3,24 @@ fakesnow/__main__.py,sha256=GDrGyNTvBFuqn_UfDjKs7b3LPtU6gDv1KwosVDrukIM,76
3
3
  fakesnow/arrow.py,sha256=MwatkdZX5AFADzXvxhBFmcRJVxbW4D39VoqLyhpTbl0,5057
4
4
  fakesnow/checks.py,sha256=N8sXldhS3u1gG32qvZ4VFlsKgavRKrQrxLiQU8am1lw,2691
5
5
  fakesnow/cli.py,sha256=9qfI-Ssr6mo8UmIlXkUAOz2z2YPBgDsrEVaZv9FjGFs,2201
6
- fakesnow/conn.py,sha256=GJ7Y2dBW2jcOCIZ0gTYS0F8OmeoD7aW6lTWHpm02hbE,5459
7
- fakesnow/cursor.py,sha256=uYf3zshauWXnKdUoVEE3YxMbc-SoVLHCUUkqwtMW8ns,20228
6
+ fakesnow/conn.py,sha256=da9ln_covsyKgdNdPXLzMTUBr72P0rRGadIDVt-kaeI,5737
7
+ fakesnow/cursor.py,sha256=1BP1rZ28JfIfJkIR_8yEFDq2FrUf93JFrrYLJoKJr14,20587
8
8
  fakesnow/expr.py,sha256=CAxuYIUkwI339DQIBzvFF0F-m1tcVGKEPA5rDTzmH9A,892
9
9
  fakesnow/fakes.py,sha256=JQTiUkkwPeQrJ8FDWhPFPK6pGwd_aR2oiOrNzCWznlM,187
10
10
  fakesnow/fixtures.py,sha256=G-NkVeruSQAJ7fvSS2fR2oysUn0Yra1pohHlOvacKEk,455
11
- fakesnow/info_schema.py,sha256=FyDcajHU0BK0Yx6JTIkWFoM0PPFm6f6Pf0B2NHLNR0M,6310
12
- fakesnow/instance.py,sha256=3cJvPRuFy19dMKXbtBLl6imzO48pEw8uTYhZyFDuwhk,3133
11
+ fakesnow/info_schema.py,sha256=_4YWnpuOFuyACr9k4iYdf2vLN7GDMG8X_pEBlC-8OmM,7269
12
+ fakesnow/instance.py,sha256=7xHJv-5-KKAI3Qm7blcvkXgkGg7WYtXEm3nUS4jLyFs,3299
13
13
  fakesnow/macros.py,sha256=pX1YJDnQOkFJSHYUjQ6ErEkYIKvFI6Ncz_au0vv1csA,265
14
14
  fakesnow/pandas_tools.py,sha256=wI203UQHC8JvDzxE_VjE1NeV4rThek2P-u52oTg2foo,3481
15
15
  fakesnow/py.typed,sha256=B-DLSjYBi7pkKjwxCSdpVj2J02wgfJr-E7B1wOUyxYU,80
16
16
  fakesnow/rowtype.py,sha256=QUp8EaXD5LT0Xv8BXk5ze4WseEn52xoJ6R05pJjs5mM,2729
17
- fakesnow/server.py,sha256=lrXk8iXioSl-qWweXLH7l6aPrcV4Bym9rjB6x_C5Fg8,4222
18
- fakesnow/transforms.py,sha256=pSv3pQlD1Y8tzXQ1rft2g5wYLcHrDRoy5EkFWgqkmec,55453
17
+ fakesnow/server.py,sha256=VpM-ZjFS4JekLESEoQTndvXqnqz8bH4ZO8lq_66-c6s,4387
18
+ fakesnow/transforms.py,sha256=dAoFFRFkJG8kcQdBPnE5w2eed4AZkh4NV3ajx1vu3A8,56444
19
19
  fakesnow/transforms_merge.py,sha256=Pg7_rwbAT_vr1U4ocBofUSyqaK8_e3qdIz_2SDm2S3s,8320
20
20
  fakesnow/variables.py,sha256=WXyPnkeNwD08gy52yF66CVe2twiYC50tztNfgXV4q1k,3032
21
- fakesnow-0.9.28.dist-info/LICENSE,sha256=kW-7NWIyaRMQiDpryfSmF2DObDZHGR1cJZ39s6B1Svg,11344
22
- fakesnow-0.9.28.dist-info/METADATA,sha256=t0B6J7rG5uyS2430rgPWieQgACfyUU9dIL5cUdYllDg,18107
23
- fakesnow-0.9.28.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
24
- fakesnow-0.9.28.dist-info/entry_points.txt,sha256=2riAUgu928ZIHawtO8EsfrMEJhi-EH-z_Vq7Q44xKPM,47
25
- fakesnow-0.9.28.dist-info/top_level.txt,sha256=500evXI1IFX9so82cizGIEMHAb_dJNPaZvd2H9dcKTA,24
26
- fakesnow-0.9.28.dist-info/RECORD,,
21
+ fakesnow-0.9.29.dist-info/LICENSE,sha256=kW-7NWIyaRMQiDpryfSmF2DObDZHGR1cJZ39s6B1Svg,11344
22
+ fakesnow-0.9.29.dist-info/METADATA,sha256=sx4qqMsuxOaaDMKR5U8A4ZT0djn9wHSrOUjCqthUoWQ,18107
23
+ fakesnow-0.9.29.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
24
+ fakesnow-0.9.29.dist-info/entry_points.txt,sha256=2riAUgu928ZIHawtO8EsfrMEJhi-EH-z_Vq7Q44xKPM,47
25
+ fakesnow-0.9.29.dist-info/top_level.txt,sha256=500evXI1IFX9so82cizGIEMHAb_dJNPaZvd2H9dcKTA,24
26
+ fakesnow-0.9.29.dist-info/RECORD,,