fakesnow 0.9.33__py3-none-any.whl → 0.9.35__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/converter.py +60 -0
- fakesnow/cursor.py +12 -4
- fakesnow/info_schema.py +4 -4
- fakesnow/instance.py +4 -2
- fakesnow/server.py +12 -1
- fakesnow/transforms/__init__.py +4 -4
- fakesnow/transforms/show.py +104 -67
- fakesnow/variables.py +1 -1
- {fakesnow-0.9.33.dist-info → fakesnow-0.9.35.dist-info}/METADATA +1 -1
- {fakesnow-0.9.33.dist-info → fakesnow-0.9.35.dist-info}/RECORD +14 -13
- {fakesnow-0.9.33.dist-info → fakesnow-0.9.35.dist-info}/WHEEL +0 -0
- {fakesnow-0.9.33.dist-info → fakesnow-0.9.35.dist-info}/entry_points.txt +0 -0
- {fakesnow-0.9.33.dist-info → fakesnow-0.9.35.dist-info}/licenses/LICENSE +0 -0
- {fakesnow-0.9.33.dist-info → fakesnow-0.9.35.dist-info}/top_level.txt +0 -0
fakesnow/converter.py
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import binascii
|
4
|
+
import datetime
|
5
|
+
from datetime import date, time, timezone
|
6
|
+
|
7
|
+
# convert server bindings from strings into python types
|
8
|
+
|
9
|
+
|
10
|
+
def from_binding(binding: dict[str, str]) -> int | bytes | bool | date | time | datetime.datetime | str:
|
11
|
+
typ = binding["type"]
|
12
|
+
value = binding["value"]
|
13
|
+
if typ == "FIXED":
|
14
|
+
return int(value)
|
15
|
+
elif typ == "BINARY":
|
16
|
+
return from_binary(value)
|
17
|
+
# TODO: not strictly needed
|
18
|
+
elif typ == "BOOLEAN":
|
19
|
+
return value.lower() == "true"
|
20
|
+
elif typ == "DATE":
|
21
|
+
return from_date(value)
|
22
|
+
elif typ == "TIME":
|
23
|
+
return from_time(value)
|
24
|
+
elif typ == "TIMESTAMP_NTZ":
|
25
|
+
return from_datetime(value)
|
26
|
+
else:
|
27
|
+
# For other types, return str
|
28
|
+
return value
|
29
|
+
|
30
|
+
|
31
|
+
def from_binary(s: str) -> bytes:
|
32
|
+
return binascii.unhexlify(s)
|
33
|
+
|
34
|
+
|
35
|
+
def from_boolean(s: str) -> bool:
|
36
|
+
return s.lower() == "true"
|
37
|
+
|
38
|
+
|
39
|
+
def from_date(s: str) -> date:
|
40
|
+
milliseconds = int(s)
|
41
|
+
seconds = milliseconds / 1000
|
42
|
+
return datetime.datetime.fromtimestamp(seconds, timezone.utc).date()
|
43
|
+
|
44
|
+
|
45
|
+
def from_time(s: str) -> time:
|
46
|
+
nanoseconds = int(s)
|
47
|
+
microseconds = nanoseconds / 1000
|
48
|
+
return (
|
49
|
+
datetime.datetime.fromtimestamp(microseconds / 1_000_000, timezone.utc)
|
50
|
+
.replace(microsecond=int(microseconds % 1_000_000))
|
51
|
+
.time()
|
52
|
+
)
|
53
|
+
|
54
|
+
|
55
|
+
def from_datetime(s: str) -> datetime.datetime:
|
56
|
+
nanoseconds = int(s)
|
57
|
+
microseconds = nanoseconds / 1000
|
58
|
+
return datetime.datetime.fromtimestamp(microseconds / 1_000_000, timezone.utc).replace(
|
59
|
+
microsecond=int(microseconds % 1_000_000)
|
60
|
+
)
|
fakesnow/cursor.py
CHANGED
@@ -139,7 +139,12 @@ class FakeSnowflakeCursor:
|
|
139
139
|
print(f"{command};{params=}" if params else f"{command};", file=sys.stderr)
|
140
140
|
|
141
141
|
command = self._inline_variables(command)
|
142
|
-
|
142
|
+
if kwargs.get("binding_params"):
|
143
|
+
# params have come via the server
|
144
|
+
params = kwargs["binding_params"]
|
145
|
+
else:
|
146
|
+
command, params = self._rewrite_with_params(command, params)
|
147
|
+
|
143
148
|
if self._conn.nop_regexes and any(re.match(p, command, re.IGNORECASE) for p in self._conn.nop_regexes):
|
144
149
|
transformed = transforms.SUCCESS_NOP
|
145
150
|
self._execute(transformed, params)
|
@@ -161,6 +166,9 @@ class FakeSnowflakeCursor:
|
|
161
166
|
# strip highlight for better readability, TODO: show pointer to start of error
|
162
167
|
msg = str(e).replace("\x1b[4m", "").replace("\x1b[0m", "")
|
163
168
|
raise snowflake.connector.errors.ProgrammingError(msg=msg, errno=1003, sqlstate="42000") from None
|
169
|
+
except NotImplementedError as e:
|
170
|
+
msg = f"{e} not implemented. Please raise an issue via https://github.com/tekumara/fakesnow/issues/new"
|
171
|
+
raise snowflake.connector.errors.ProgrammingError(msg=msg, errno=9999, sqlstate="99999") from e
|
164
172
|
|
165
173
|
def check_db_and_schema(self, expression: exp.Expression) -> None:
|
166
174
|
no_database, no_schema = checks.is_unqualified_table_expression(expression)
|
@@ -233,7 +241,7 @@ class FakeSnowflakeCursor:
|
|
233
241
|
.transform(transforms.show_procedures)
|
234
242
|
.transform(transforms.show_warehouses)
|
235
243
|
.transform(lambda e: transforms.show_schemas(e, self._conn.database))
|
236
|
-
.transform(lambda e: transforms.
|
244
|
+
.transform(lambda e: transforms.show_tables_etc(e, self._conn.database))
|
237
245
|
.transform(lambda e: transforms.show_columns(e, self._conn.database))
|
238
246
|
# TODO collapse into a single show_keys function
|
239
247
|
.transform(lambda e: transforms.show_keys(e, self._conn.database, kind="PRIMARY"))
|
@@ -384,7 +392,7 @@ class FakeSnowflakeCursor:
|
|
384
392
|
self._sfqid = str(uuid.uuid4())
|
385
393
|
|
386
394
|
self._last_sql = result_sql or sql
|
387
|
-
self._last_params = params
|
395
|
+
self._last_params = None if result_sql else params
|
388
396
|
|
389
397
|
def _log_sql(self, sql: str, params: Sequence[Any] | dict[Any, Any] | None = None) -> None:
|
390
398
|
if (fs_debug := os.environ.get("FAKESNOW_DEBUG")) and fs_debug != "snowflake":
|
@@ -398,7 +406,7 @@ class FakeSnowflakeCursor:
|
|
398
406
|
) -> FakeSnowflakeCursor:
|
399
407
|
if isinstance(seqparams, dict):
|
400
408
|
# see https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-api
|
401
|
-
raise NotImplementedError("dict params
|
409
|
+
raise NotImplementedError("executemany dict params")
|
402
410
|
|
403
411
|
# TODO: support insert optimisations
|
404
412
|
# the snowflake connector will optimise inserts into a single query
|
fakesnow/info_schema.py
CHANGED
@@ -34,8 +34,8 @@ create table if not exists _fs_global._fs_information_schema._fs_columns_ext (
|
|
34
34
|
"""
|
35
35
|
|
36
36
|
# replicates the output structure of https://docs.snowflake.com/en/sql-reference/sql/show-users
|
37
|
-
|
38
|
-
create table if not exists _fs_global._fs_information_schema.
|
37
|
+
SQL_CREATE_GLOBAL_INFORMATION_SCHEMA_USERS_TABLE = """
|
38
|
+
create table if not exists _fs_global._fs_information_schema._fs_users (
|
39
39
|
name varchar,
|
40
40
|
created_on TIMESTAMPTZ,
|
41
41
|
login_name varchar,
|
@@ -196,13 +196,13 @@ def per_db_creation_sql(catalog: str) -> str:
|
|
196
196
|
"""
|
197
197
|
|
198
198
|
|
199
|
-
def fs_global_creation_sql(
|
199
|
+
def fs_global_creation_sql() -> str:
|
200
200
|
return f"""
|
201
201
|
{SQL_CREATE_GLOBAL_FS_INFORMATION_SCHEMA};
|
202
202
|
{SQL_CREATE_GLOBAL_INFORMATION_SCHEMA_TABLES_EXT};
|
203
203
|
{SQL_CREATE_GLOBAL_INFORMATION_SCHEMA_COLUMNS_EXT};
|
204
204
|
{SQL_CREATE_GLOBAL_INFORMATION_SCHEMA_COLUMNS_VIEW};
|
205
|
-
{
|
205
|
+
{SQL_CREATE_GLOBAL_INFORMATION_SCHEMA_USERS_TABLE};
|
206
206
|
"""
|
207
207
|
|
208
208
|
|
fakesnow/instance.py
CHANGED
@@ -7,6 +7,7 @@ import duckdb
|
|
7
7
|
|
8
8
|
import fakesnow.fakes as fakes
|
9
9
|
from fakesnow import info_schema
|
10
|
+
from fakesnow.transforms import show
|
10
11
|
|
11
12
|
GLOBAL_DATABASE_NAME = "_fs_global"
|
12
13
|
|
@@ -28,8 +29,9 @@ class FakeSnow:
|
|
28
29
|
|
29
30
|
# create a "global" database for storing objects which span databases.
|
30
31
|
self.duck_conn.execute(f"ATTACH IF NOT EXISTS ':memory:' AS {GLOBAL_DATABASE_NAME}")
|
31
|
-
# create the info schema extensions
|
32
|
-
self.duck_conn.execute(info_schema.fs_global_creation_sql(
|
32
|
+
# create the info schema extensions and show views
|
33
|
+
self.duck_conn.execute(info_schema.fs_global_creation_sql())
|
34
|
+
self.duck_conn.execute(show.fs_global_creation_sql())
|
33
35
|
|
34
36
|
# use UTC instead of local time zone for consistent testing
|
35
37
|
self.duck_conn.execute("SET GLOBAL TimeZone = 'UTC'")
|
fakesnow/server.py
CHANGED
@@ -16,6 +16,7 @@ from starlette.responses import JSONResponse
|
|
16
16
|
from starlette.routing import Route
|
17
17
|
|
18
18
|
from fakesnow.arrow import to_ipc, to_sf
|
19
|
+
from fakesnow.converter import from_binding
|
19
20
|
from fakesnow.fakes import FakeSnowflakeConnection
|
20
21
|
from fakesnow.instance import FakeSnow
|
21
22
|
from fakesnow.rowtype import describe_as_rowtype
|
@@ -77,9 +78,16 @@ async def query_request(request: Request) -> JSONResponse:
|
|
77
78
|
|
78
79
|
sql_text = body_json["sqlText"]
|
79
80
|
|
81
|
+
if bindings := body_json.get("bindings"):
|
82
|
+
# Convert parameters like {'1': {'type': 'FIXED', 'value': '10'}, ...} to tuple (10, ...)
|
83
|
+
params = tuple(from_binding(bindings[str(pos)]) for pos in range(1, len(bindings) + 1))
|
84
|
+
logger.debug(f"Bindings: {params}")
|
85
|
+
else:
|
86
|
+
params = None
|
87
|
+
|
80
88
|
try:
|
81
89
|
# only a single sql statement is sent at a time by the python snowflake connector
|
82
|
-
cur = await run_in_threadpool(conn.cursor().execute, sql_text)
|
90
|
+
cur = await run_in_threadpool(conn.cursor().execute, sql_text, binding_params=params)
|
83
91
|
rowtype = describe_as_rowtype(cur._describe_last_sql()) # noqa: SLF001
|
84
92
|
|
85
93
|
except snowflake.connector.errors.ProgrammingError as e:
|
@@ -113,6 +121,9 @@ async def query_request(request: Request) -> JSONResponse:
|
|
113
121
|
return JSONResponse(
|
114
122
|
{
|
115
123
|
"data": {
|
124
|
+
"parameters": [
|
125
|
+
{"name": "TIMEZONE", "value": "Etc/UTC"},
|
126
|
+
],
|
116
127
|
"rowtype": rowtype,
|
117
128
|
"rowsetBase64": rowset_b64,
|
118
129
|
"total": cur._rowcount, # noqa: SLF001
|
fakesnow/transforms/__init__.py
CHANGED
@@ -13,9 +13,9 @@ from fakesnow.transforms.show import (
|
|
13
13
|
show_databases as show_databases,
|
14
14
|
show_functions as show_functions,
|
15
15
|
show_keys as show_keys,
|
16
|
-
show_objects_tables as show_objects_tables,
|
17
16
|
show_procedures as show_procedures,
|
18
17
|
show_schemas as show_schemas,
|
18
|
+
show_tables_etc as show_tables_etc,
|
19
19
|
show_users as show_users,
|
20
20
|
show_warehouses as show_warehouses,
|
21
21
|
)
|
@@ -837,7 +837,7 @@ def regex_replace(expression: exp.Expression) -> exp.Expression:
|
|
837
837
|
if len(expression.args) > 3:
|
838
838
|
# see https://docs.snowflake.com/en/sql-reference/functions/regexp_replace
|
839
839
|
raise NotImplementedError(
|
840
|
-
"REGEXP_REPLACE with additional parameters (eg: <position>, <occurrence>, <parameters>)
|
840
|
+
"REGEXP_REPLACE with additional parameters (eg: <position>, <occurrence>, <parameters>)"
|
841
841
|
)
|
842
842
|
|
843
843
|
# pattern: snowflake requires escaping backslashes in single-quoted string constants, but duckdb doesn't
|
@@ -1316,9 +1316,9 @@ def create_user(expression: exp.Expression) -> exp.Expression:
|
|
1316
1316
|
if sub_exp.upper().startswith("USER"):
|
1317
1317
|
_, name, *ignored = sub_exp.split(" ")
|
1318
1318
|
if ignored:
|
1319
|
-
raise NotImplementedError(f"`CREATE USER` with {ignored}
|
1319
|
+
raise NotImplementedError(f"`CREATE USER` with {ignored}")
|
1320
1320
|
return sqlglot.parse_one(
|
1321
|
-
f"INSERT INTO _fs_global._fs_information_schema.
|
1321
|
+
f"INSERT INTO _fs_global._fs_information_schema._fs_users (name) VALUES ('{name}')", read="duckdb"
|
1322
1322
|
)
|
1323
1323
|
|
1324
1324
|
return expression
|
fakesnow/transforms/show.py
CHANGED
@@ -6,6 +6,14 @@ import sqlglot
|
|
6
6
|
from sqlglot import exp
|
7
7
|
|
8
8
|
|
9
|
+
def fs_global_creation_sql() -> str:
|
10
|
+
return f"""
|
11
|
+
{SQL_CREATE_VIEW_SHOW_OBJECTS};
|
12
|
+
{SQL_CREATE_VIEW_SHOW_TABLES};
|
13
|
+
{SQL_CREATE_VIEW_SHOW_VIEWS};
|
14
|
+
"""
|
15
|
+
|
16
|
+
|
9
17
|
def show_columns(
|
10
18
|
expression: exp.Expression, current_database: str | None = None, current_schema: str | None = None
|
11
19
|
) -> exp.Expression:
|
@@ -283,22 +291,99 @@ def show_keys(
|
|
283
291
|
|
284
292
|
statement += f"AND table_name = '{table.name}' "
|
285
293
|
else:
|
286
|
-
raise NotImplementedError(f"SHOW PRIMARY KEYS with {scope_kind}
|
294
|
+
raise NotImplementedError(f"SHOW PRIMARY KEYS with {scope_kind}")
|
287
295
|
return sqlglot.parse_one(statement)
|
288
296
|
return expression
|
289
297
|
|
290
298
|
|
291
|
-
|
292
|
-
|
299
|
+
# see https://docs.snowflake.com/en/sql-reference/sql/show-objects
|
300
|
+
SQL_CREATE_VIEW_SHOW_OBJECTS = """
|
301
|
+
create view if not exists _fs_global._fs_information_schema._fs_show_objects as
|
302
|
+
select
|
303
|
+
to_timestamp(0)::timestamptz as created_on,
|
304
|
+
table_name as name,
|
305
|
+
case when table_type='BASE TABLE' then 'TABLE' else table_type end as 'kind',
|
306
|
+
table_catalog as database_name,
|
307
|
+
table_schema as schema_name,
|
308
|
+
'' as comment,
|
309
|
+
'' as cluster_by,
|
310
|
+
-- TODO: implement rows and bytes as rows * 1024
|
311
|
+
0 as rows,
|
312
|
+
0 as bytes,
|
313
|
+
'SYSADMIN' as owner,
|
314
|
+
1 as retention_time,
|
315
|
+
'ROLE' as owner_role_type,
|
316
|
+
null as budget,
|
317
|
+
'N' as is_hybrid,
|
318
|
+
'N' as is_dynamic
|
319
|
+
from information_schema.tables
|
320
|
+
where not (table_schema == '_fs_information_schema')
|
321
|
+
"""
|
293
322
|
|
294
|
-
|
295
|
-
|
296
|
-
|
323
|
+
# see https://docs.snowflake.com/en/sql-reference/sql/show-tables
|
324
|
+
SQL_CREATE_VIEW_SHOW_TABLES = """
|
325
|
+
create view if not exists _fs_global._fs_information_schema._fs_show_tables as
|
326
|
+
select
|
327
|
+
to_timestamp(0)::timestamptz as created_on,
|
328
|
+
table_name as name,
|
329
|
+
'TABLE' as kind,
|
330
|
+
table_catalog as database_name,
|
331
|
+
table_schema as schema_name,
|
332
|
+
'' as comment,
|
333
|
+
'' as cluster_by,
|
334
|
+
-- TODO: implement rows and bytes as rows * 1024
|
335
|
+
0 as rows,
|
336
|
+
0 as bytes,
|
337
|
+
'SYSADMIN' as owner,
|
338
|
+
1 as retention_time,
|
339
|
+
'OFF' as automatic_clustering,
|
340
|
+
'OFF' as change_tracking,
|
341
|
+
'OFF' as search_optimization,
|
342
|
+
null as search_optimization_progress,
|
343
|
+
null as search_optimization_bytes,
|
344
|
+
'N' as is_external,
|
345
|
+
'N' as enable_schema_evolution,
|
346
|
+
'ROLE' as owner_role_type,
|
347
|
+
'N' as is_event,
|
348
|
+
null as budget,
|
349
|
+
'N' as is_hybrid,
|
350
|
+
'N' as is_iceberg,
|
351
|
+
'N' as is_dynamic,
|
352
|
+
'N' as is_immutable
|
353
|
+
from information_schema.tables
|
354
|
+
where not (table_schema == '_fs_information_schema')
|
355
|
+
and table_type = 'BASE TABLE'
|
356
|
+
"""
|
357
|
+
|
358
|
+
# see https://docs.snowflake.com/en/sql-reference/sql/show-views
|
359
|
+
SQL_CREATE_VIEW_SHOW_VIEWS = """
|
360
|
+
create view if not exists _fs_global._fs_information_schema._fs_show_views as
|
361
|
+
select
|
362
|
+
to_timestamp(0)::timestamptz as created_on,
|
363
|
+
table_name as name,
|
364
|
+
'' as reserved,
|
365
|
+
table_catalog as database_name,
|
366
|
+
table_schema as schema_name,
|
367
|
+
'SYSADMIN' as owner,
|
368
|
+
'' as comment,
|
369
|
+
view_definition as text,
|
370
|
+
false as is_secure,
|
371
|
+
false as is_materialized,
|
372
|
+
'ROLE' as owner_role_type,
|
373
|
+
'OFF' as change_tracking
|
374
|
+
from information_schema.views
|
375
|
+
where not table_catalog in ('system')
|
376
|
+
and not table_schema in ('main', '_fs_information_schema')
|
377
|
+
"""
|
378
|
+
|
379
|
+
|
380
|
+
def show_tables_etc(expression: exp.Expression, current_database: str | None = None) -> exp.Expression:
|
381
|
+
"""Transform SHOW OBJECTS/TABLES/VIEWS to a query against the _fs_information_schema views."""
|
297
382
|
if not (
|
298
383
|
isinstance(expression, exp.Show)
|
299
384
|
and isinstance(expression.this, str)
|
300
385
|
and (show := expression.this.upper())
|
301
|
-
and show in {"OBJECTS", "TABLES"}
|
386
|
+
and show in {"OBJECTS", "TABLES", "VIEWS"}
|
302
387
|
):
|
303
388
|
return expression
|
304
389
|
|
@@ -316,76 +401,28 @@ def show_objects_tables(expression: exp.Expression, current_database: str | None
|
|
316
401
|
catalog = None
|
317
402
|
schema = None
|
318
403
|
|
319
|
-
|
320
|
-
"
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
]
|
326
|
-
if not expression.args["terse"]:
|
327
|
-
if show == "OBJECTS":
|
328
|
-
columns.extend(
|
329
|
-
[
|
330
|
-
"'' as 'comment'",
|
331
|
-
"'' as 'cluster_by'",
|
332
|
-
# TODO: implement rows and bytes as rows * 1024
|
333
|
-
"0 as 'rows'",
|
334
|
-
"0 as 'bytes'",
|
335
|
-
"'SYSADMIN' as 'owner'",
|
336
|
-
"1 as 'retention_time'",
|
337
|
-
"'ROLE' as 'owner_role_type'",
|
338
|
-
"null as 'budget'",
|
339
|
-
"'N' as 'is_hybrid'",
|
340
|
-
"'N' as 'is_dynamic'",
|
341
|
-
]
|
342
|
-
)
|
343
|
-
else:
|
344
|
-
# show == "TABLES"
|
345
|
-
columns.extend(
|
346
|
-
[
|
347
|
-
"'' as 'comment'",
|
348
|
-
"'' as 'cluster_by'",
|
349
|
-
# TODO: implement rows and bytes as rows * 1024
|
350
|
-
"0 as 'rows'",
|
351
|
-
"0 as 'bytes'",
|
352
|
-
"'SYSADMIN' as 'owner'",
|
353
|
-
"1 as 'retention_time'",
|
354
|
-
"'OFF' as 'automatic_clustering'",
|
355
|
-
"'OFF' as 'change_tracking'",
|
356
|
-
"'OFF' as 'search_optimization'",
|
357
|
-
"null as 'search_optimization_progress'",
|
358
|
-
"null as 'search_optimization_bytes'",
|
359
|
-
"'N' as 'is_external'",
|
360
|
-
"'N' as 'enable_schema_evolution'",
|
361
|
-
"'ROLE' as 'owner_role_type'",
|
362
|
-
"'N' as 'is_event'",
|
363
|
-
"null as 'budget'",
|
364
|
-
"'N' as 'is_hybrid'",
|
365
|
-
"'N' as 'is_iceberg'",
|
366
|
-
"'N' as 'is_dynamic'",
|
367
|
-
"'N' as 'is_immutable'",
|
368
|
-
]
|
369
|
-
)
|
370
|
-
|
404
|
+
if expression.args["terse"] and show == "VIEWS":
|
405
|
+
columns = ["created_on, name, 'VIEW' as kind, database_name, schema_name"]
|
406
|
+
elif expression.args["terse"]:
|
407
|
+
columns = ["created_on, name, kind, database_name, schema_name"]
|
408
|
+
else:
|
409
|
+
columns = ["*"]
|
371
410
|
columns_clause = ", ".join(columns)
|
372
411
|
|
373
|
-
where = ["
|
374
|
-
if show == "TABLES":
|
375
|
-
where.append("table_type = 'BASE TABLE'")
|
412
|
+
where = ["1=1"]
|
376
413
|
if catalog:
|
377
|
-
where.append(f"
|
414
|
+
where.append(f"database_name = '{catalog}'")
|
378
415
|
if schema:
|
379
|
-
where.append(f"
|
416
|
+
where.append(f"schema_name = '{schema}'")
|
380
417
|
if (like := expression.args.get("like")) and isinstance(like, exp.Expression):
|
381
|
-
where.append(f"
|
418
|
+
where.append(f"name ilike {like.sql()}")
|
382
419
|
where_clause = " AND ".join(where)
|
383
420
|
|
384
421
|
limit = limit.sql() if (limit := expression.args.get("limit")) and isinstance(limit, exp.Expression) else ""
|
385
422
|
|
386
423
|
query = f"""
|
387
424
|
SELECT {columns_clause}
|
388
|
-
from
|
425
|
+
from _fs_global._fs_information_schema._fs_show_{show.lower()}
|
389
426
|
where {where_clause}
|
390
427
|
{limit}
|
391
428
|
"""
|
@@ -471,6 +508,6 @@ def show_users(expression: exp.Expression) -> exp.Expression:
|
|
471
508
|
https://docs.snowflake.com/en/sql-reference/sql/show-users
|
472
509
|
"""
|
473
510
|
if isinstance(expression, exp.Show) and isinstance(expression.this, str) and expression.this.upper() == "USERS":
|
474
|
-
return sqlglot.parse_one("SELECT * FROM _fs_global._fs_information_schema.
|
511
|
+
return sqlglot.parse_one("SELECT * FROM _fs_global._fs_information_schema._fs_users", read="duckdb")
|
475
512
|
|
476
513
|
return expression
|
fakesnow/variables.py
CHANGED
@@ -45,7 +45,7 @@ class Variables:
|
|
45
45
|
self._set(name, value)
|
46
46
|
else:
|
47
47
|
# Haven't been able to produce this in tests yet due to UNSET being parsed as an Alias expression.
|
48
|
-
raise NotImplementedError("UNSET
|
48
|
+
raise NotImplementedError("UNSET")
|
49
49
|
elif self._is_unset_expression(expr): # Unfortunately UNSET varname; is parsed as an Alias expression :(
|
50
50
|
alias = expr.args.get("alias")
|
51
51
|
assert alias, "UNSET without value in alias attribute is unexpected."
|
@@ -4,25 +4,26 @@ fakesnow/arrow.py,sha256=XjTpFyLrD9jULWOtPgpr0RyNMmO6a5yi82y6ivi2CCI,4884
|
|
4
4
|
fakesnow/checks.py,sha256=be-xo0oMoAUVhlMDCu1_Rkoh_L8p_p8qo9P6reJSHIQ,2874
|
5
5
|
fakesnow/cli.py,sha256=9qfI-Ssr6mo8UmIlXkUAOz2z2YPBgDsrEVaZv9FjGFs,2201
|
6
6
|
fakesnow/conn.py,sha256=2WClMmUgfQkQA2hFQjfMP3R-85TbTbZh_8Y1tCdcerA,6053
|
7
|
-
fakesnow/
|
7
|
+
fakesnow/converter.py,sha256=xoBFnfBbGWQyUQAVr6zi-RyglU8A7A3GSlwLPkH1dzI,1621
|
8
|
+
fakesnow/cursor.py,sha256=e-nY-w25xmLs1jdhIbO6mwzBHVIUKYIkzR37Wnf3Cfc,22068
|
8
9
|
fakesnow/expr.py,sha256=CAxuYIUkwI339DQIBzvFF0F-m1tcVGKEPA5rDTzmH9A,892
|
9
10
|
fakesnow/fakes.py,sha256=JQTiUkkwPeQrJ8FDWhPFPK6pGwd_aR2oiOrNzCWznlM,187
|
10
11
|
fakesnow/fixtures.py,sha256=G-NkVeruSQAJ7fvSS2fR2oysUn0Yra1pohHlOvacKEk,455
|
11
|
-
fakesnow/info_schema.py,sha256=
|
12
|
-
fakesnow/instance.py,sha256=
|
12
|
+
fakesnow/info_schema.py,sha256=AYmTIHxk5Y6xdMTgttgBL1V0VO8qiM2T1-gKwkLmWDs,8720
|
13
|
+
fakesnow/instance.py,sha256=OKoYXwaI6kL9HQpnHx44yzpON_xNfuIT_F4oJNF_XXQ,2114
|
13
14
|
fakesnow/macros.py,sha256=pX1YJDnQOkFJSHYUjQ6ErEkYIKvFI6Ncz_au0vv1csA,265
|
14
15
|
fakesnow/pandas_tools.py,sha256=wI203UQHC8JvDzxE_VjE1NeV4rThek2P-u52oTg2foo,3481
|
15
16
|
fakesnow/py.typed,sha256=B-DLSjYBi7pkKjwxCSdpVj2J02wgfJr-E7B1wOUyxYU,80
|
16
17
|
fakesnow/rowtype.py,sha256=QUp8EaXD5LT0Xv8BXk5ze4WseEn52xoJ6R05pJjs5mM,2729
|
17
|
-
fakesnow/server.py,sha256=
|
18
|
-
fakesnow/variables.py,sha256=
|
19
|
-
fakesnow/transforms/__init__.py,sha256=
|
18
|
+
fakesnow/server.py,sha256=6EMdNMxL0DgfMNXfFBqRFYmRThbuEVFFpa_1sCW-rZ4,6519
|
19
|
+
fakesnow/variables.py,sha256=C3y_9u7LuVtARkpcim3ihgVWg6KKdz1hSVeW4YI7oL4,3014
|
20
|
+
fakesnow/transforms/__init__.py,sha256=eJ_XH9pKO4Mxb7MKVgJuo6Wea-LYahF1dc_py5lPHAY,49471
|
20
21
|
fakesnow/transforms/merge.py,sha256=Pg7_rwbAT_vr1U4ocBofUSyqaK8_e3qdIz_2SDm2S3s,8320
|
21
|
-
fakesnow/transforms/show.py,sha256=
|
22
|
-
fakesnow-0.9.
|
22
|
+
fakesnow/transforms/show.py,sha256=0NjuLQjodrukfUw8mcxcAmtBkV_6r02mA3nuE3ad3rE,17458
|
23
|
+
fakesnow-0.9.35.dist-info/licenses/LICENSE,sha256=kW-7NWIyaRMQiDpryfSmF2DObDZHGR1cJZ39s6B1Svg,11344
|
23
24
|
tools/decode.py,sha256=kC5kUvLQxdCkMRsnH6BqCajlKxKeN77w6rwCKsY6gqU,1781
|
24
|
-
fakesnow-0.9.
|
25
|
-
fakesnow-0.9.
|
26
|
-
fakesnow-0.9.
|
27
|
-
fakesnow-0.9.
|
28
|
-
fakesnow-0.9.
|
25
|
+
fakesnow-0.9.35.dist-info/METADATA,sha256=D2aDpQUAIuSHEf6W5f1m84IG3BBDEvvMN5fU_FiWl3c,18128
|
26
|
+
fakesnow-0.9.35.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
27
|
+
fakesnow-0.9.35.dist-info/entry_points.txt,sha256=2riAUgu928ZIHawtO8EsfrMEJhi-EH-z_Vq7Q44xKPM,47
|
28
|
+
fakesnow-0.9.35.dist-info/top_level.txt,sha256=Yos7YveA3f03xVYuURqnBsfMV2DePXfu_yGcsj3pPzI,30
|
29
|
+
fakesnow-0.9.35.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|