linkml-store 0.0.0__py3-none-any.whl → 0.1.7__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.
Potentially problematic release.
This version of linkml-store might be problematic. Click here for more details.
- linkml_store/api/__init__.py +2 -2
- linkml_store/api/client.py +113 -8
- linkml_store/api/collection.py +272 -34
- linkml_store/api/config.py +101 -0
- linkml_store/api/database.py +282 -18
- linkml_store/api/queries.py +12 -1
- linkml_store/api/stores/chromadb/__init__.py +3 -0
- linkml_store/api/stores/chromadb/chromadb_collection.py +121 -0
- linkml_store/api/stores/chromadb/chromadb_database.py +89 -0
- linkml_store/api/stores/duckdb/__init__.py +7 -0
- linkml_store/api/stores/duckdb/duckdb_collection.py +47 -14
- linkml_store/api/stores/duckdb/duckdb_database.py +38 -47
- linkml_store/api/stores/hdf5/__init__.py +0 -0
- linkml_store/api/stores/hdf5/hdf5_collection.py +104 -0
- linkml_store/api/stores/hdf5/hdf5_database.py +79 -0
- linkml_store/api/stores/mongodb/mongodb_collection.py +92 -40
- linkml_store/api/stores/mongodb/mongodb_database.py +58 -67
- linkml_store/api/stores/solr/__init__.py +3 -0
- linkml_store/api/stores/solr/solr_collection.py +133 -0
- linkml_store/api/stores/solr/solr_database.py +83 -0
- linkml_store/api/stores/solr/solr_utils.py +0 -0
- linkml_store/cli.py +369 -0
- linkml_store/index/__init__.py +33 -0
- linkml_store/index/implementations/{llm_index.py → llm_indexer.py} +2 -2
- linkml_store/index/implementations/{simple_index.py → simple_indexer.py} +6 -3
- linkml_store/index/{index.py → indexer.py} +7 -4
- linkml_store/utils/format_utils.py +93 -0
- linkml_store/utils/object_utils.py +81 -0
- linkml_store/utils/sql_utils.py +46 -7
- {linkml_store-0.0.0.dist-info → linkml_store-0.1.7.dist-info}/METADATA +17 -6
- linkml_store-0.1.7.dist-info/RECORD +42 -0
- linkml_store-0.1.7.dist-info/entry_points.txt +3 -0
- linkml_store/api/metadata.py +0 -5
- linkml_store-0.0.0.dist-info/RECORD +0 -29
- linkml_store-0.0.0.dist-info/entry_points.txt +0 -3
- {linkml_store-0.0.0.dist-info → linkml_store-0.1.7.dist-info}/LICENSE +0 -0
- {linkml_store-0.0.0.dist-info → linkml_store-0.1.7.dist-info}/WHEEL +0 -0
linkml_store/utils/sql_utils.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import Optional, Tuple, Type, Union
|
|
2
|
+
from typing import Any, Optional, Tuple, Type, Union
|
|
3
3
|
|
|
4
4
|
import sqlalchemy
|
|
5
5
|
import sqlalchemy.sql.sqltypes as sqlt
|
|
@@ -17,6 +17,12 @@ TYPE_MAP = {
|
|
|
17
17
|
sqlt.FLOAT: "float",
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
OP_MAP = {
|
|
21
|
+
"eq": "=",
|
|
22
|
+
"in": "ARRAY_CONTAINS",
|
|
23
|
+
"$contains": "ARRAY_CONTAINS",
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
|
|
21
27
|
def _map_type(typ: Type) -> str:
|
|
22
28
|
for k, v in TYPE_MAP.items():
|
|
@@ -33,13 +39,40 @@ def where_clause_to_sql(query: Query) -> str:
|
|
|
33
39
|
elif isinstance(query.where_clause, list):
|
|
34
40
|
where_clause_sql = " AND ".join(query.where_clause)
|
|
35
41
|
elif isinstance(query.where_clause, dict):
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
conjs = []
|
|
43
|
+
for k, v in query.where_clause.items():
|
|
44
|
+
conjs.extend(col_val_constraints_to_conjs(k, v))
|
|
45
|
+
where_clause_sql = " AND ".join(conjs)
|
|
46
|
+
|
|
38
47
|
else:
|
|
39
48
|
raise ValueError(f"Invalid where_clause type: {type(query.where_clause)}")
|
|
40
49
|
return "WHERE " + where_clause_sql
|
|
41
50
|
|
|
42
51
|
|
|
52
|
+
def col_val_constraints_to_conjs(col_name: str, val_constraints: Any) -> list:
|
|
53
|
+
if val_constraints is None:
|
|
54
|
+
return []
|
|
55
|
+
|
|
56
|
+
def _quote(v: Any):
|
|
57
|
+
if isinstance(v, str):
|
|
58
|
+
# escape internal vs
|
|
59
|
+
v = v.replace("'", "''")
|
|
60
|
+
return f"'{v}'"
|
|
61
|
+
else:
|
|
62
|
+
return v
|
|
63
|
+
|
|
64
|
+
if isinstance(val_constraints, dict):
|
|
65
|
+
conjs = []
|
|
66
|
+
for k, v in val_constraints.items():
|
|
67
|
+
if k in OP_MAP:
|
|
68
|
+
conjs.append(f"{OP_MAP[k]}({col_name}, {_quote(v)})")
|
|
69
|
+
else:
|
|
70
|
+
conjs.append(f"{col_name} {k} {_quote(v)}")
|
|
71
|
+
return conjs
|
|
72
|
+
else:
|
|
73
|
+
return [f"{col_name} = {_quote(val_constraints)}"]
|
|
74
|
+
|
|
75
|
+
|
|
43
76
|
def query_to_sql(query: Query, count=False, limit=None, offset: Optional[int] = None):
|
|
44
77
|
select_cols = query.select_cols if query.select_cols else ["*"]
|
|
45
78
|
if count:
|
|
@@ -67,7 +100,7 @@ def query_to_sql(query: Query, count=False, limit=None, offset: Optional[int] =
|
|
|
67
100
|
return "\n".join(sql_str)
|
|
68
101
|
|
|
69
102
|
|
|
70
|
-
def facet_count_sql(query: Query, facet_column: Union[str, Tuple[str, ...]], multivalued=False) -> str:
|
|
103
|
+
def facet_count_sql(query: Query, facet_column: Union[str, Tuple[str, ...]], multivalued=False, limit=100) -> str:
|
|
71
104
|
# Create a modified WHERE clause that excludes conditions directly related to facet_column
|
|
72
105
|
modified_where = None
|
|
73
106
|
if query.where_clause:
|
|
@@ -82,12 +115,18 @@ def facet_count_sql(query: Query, facet_column: Union[str, Tuple[str, ...]], mul
|
|
|
82
115
|
facet_column = ", ".join(facet_column)
|
|
83
116
|
from_table = query.from_table
|
|
84
117
|
if multivalued:
|
|
85
|
-
from_table = f"(SELECT UNNEST({facet_column}) as {facet_column} FROM {query.from_table}
|
|
118
|
+
from_table = f"(SELECT UNNEST({facet_column}) as {facet_column} FROM {query.from_table}"
|
|
119
|
+
from_table += f" {modified_where}" if modified_where else ""
|
|
120
|
+
from_table += ")"
|
|
121
|
+
else:
|
|
122
|
+
from_table += f" {modified_where}" if modified_where else ""
|
|
86
123
|
sql_str = [f"SELECT {facet_column}, COUNT(*) as count", f"FROM {from_table}"]
|
|
87
|
-
if modified_where:
|
|
88
|
-
|
|
124
|
+
# if modified_where:
|
|
125
|
+
# sql_str.append(f"{modified_where}")
|
|
89
126
|
sql_str.append(f"GROUP BY {facet_column}")
|
|
90
127
|
sql_str.append("ORDER BY count DESC") # Optional, order by count for convenience
|
|
128
|
+
if limit is not None:
|
|
129
|
+
sql_str.append(f"LIMIT {limit}")
|
|
91
130
|
return "\n".join(sql_str)
|
|
92
131
|
|
|
93
132
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: linkml-store
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.1.7
|
|
4
4
|
Summary: linkml-store
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Author 1
|
|
@@ -14,17 +14,25 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
15
|
Provides-Extra: analytics
|
|
16
16
|
Provides-Extra: app
|
|
17
|
+
Provides-Extra: chromadb
|
|
18
|
+
Provides-Extra: h5py
|
|
17
19
|
Provides-Extra: llm
|
|
20
|
+
Provides-Extra: map
|
|
18
21
|
Provides-Extra: mongodb
|
|
19
22
|
Provides-Extra: tests
|
|
23
|
+
Provides-Extra: validation
|
|
20
24
|
Requires-Dist: black (>=24.0.0) ; extra == "tests"
|
|
25
|
+
Requires-Dist: chromadb ; extra == "chromadb"
|
|
21
26
|
Requires-Dist: click
|
|
22
27
|
Requires-Dist: duckdb (>=0.10.1,<0.11.0)
|
|
23
|
-
Requires-Dist: duckdb-engine (>=0.11.2
|
|
28
|
+
Requires-Dist: duckdb-engine (>=0.11.2)
|
|
29
|
+
Requires-Dist: h5py ; extra == "h5py"
|
|
30
|
+
Requires-Dist: linkml ; extra == "validation"
|
|
24
31
|
Requires-Dist: linkml-runtime (>=1.7.5,<2.0.0)
|
|
32
|
+
Requires-Dist: linkml_map ; extra == "map"
|
|
25
33
|
Requires-Dist: llm ; extra == "llm"
|
|
26
34
|
Requires-Dist: matplotlib ; extra == "analytics"
|
|
27
|
-
Requires-Dist: pandas (>=2.2.1
|
|
35
|
+
Requires-Dist: pandas (>=2.2.1) ; extra == "analytics"
|
|
28
36
|
Requires-Dist: plotly ; extra == "analytics"
|
|
29
37
|
Requires-Dist: pydantic (>=2.0.0,<3.0.0)
|
|
30
38
|
Requires-Dist: pymongo ; extra == "mongodb"
|
|
@@ -36,9 +44,12 @@ Description-Content-Type: text/markdown
|
|
|
36
44
|
|
|
37
45
|
# linkml-store
|
|
38
46
|
|
|
39
|
-
|
|
47
|
+
An integration layer for multiple database backends
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
Currently this software is alpha, and has only been tested with DuckDB backends. See the Tutorial for more information.
|
|
42
50
|
|
|
43
|
-
|
|
51
|
+
There is also experimental support for vector-based indexing using OpenAI test embedding via the `llm` library.
|
|
52
|
+
|
|
53
|
+
The goals of this project are to provide high level access to data stored in heterogeneous databases,
|
|
54
|
+
with optional schema management using LinkML.
|
|
44
55
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
linkml_store/__init__.py,sha256=jlU6WOUAn8cKIhzbTULmBTWpW9gZdEt7q_RI6KZN1bY,118
|
|
2
|
+
linkml_store/api/__init__.py,sha256=3CelcFEFz0y3MkQAzhQ9JxHIt1zFk6nYZxSmYTo8YZE,226
|
|
3
|
+
linkml_store/api/client.py,sha256=fN6iQONazbSH_FJIPqHzISKoHRC0vi_XudaFVX01l-s,7709
|
|
4
|
+
linkml_store/api/collection.py,sha256=rm-C-GZ6leib_uEDPz-a0exVyN5pD08E8cZPsXmCG9I,18460
|
|
5
|
+
linkml_store/api/config.py,sha256=9raqXrZ-4U-6DPI2Dnc3O9UVdO91lOAPjdtS82zALSg,3409
|
|
6
|
+
linkml_store/api/database.py,sha256=TYq2JQFxV7ZtaRnR-o4Le984mna3yXynrUm6r9pGVsE,17098
|
|
7
|
+
linkml_store/api/queries.py,sha256=w0qnNeCH6pC9WTGoEQYd300MF6o0G3atz2YxN3WecAs,2028
|
|
8
|
+
linkml_store/api/stores/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
linkml_store/api/stores/chromadb/__init__.py,sha256=uqFsViPH0ckR-mjrttTBNEVu-rp7ub-Za7Muw3BZK8A,46
|
|
10
|
+
linkml_store/api/stores/chromadb/chromadb_collection.py,sha256=RQUZx5oeotkzNihg-dlSevkiTiKY1d9x0bS63HF80W4,4270
|
|
11
|
+
linkml_store/api/stores/chromadb/chromadb_database.py,sha256=dZA3LQE8-ZMhJQOzsUFyxehnKpFF7adR182aggfkaFY,3205
|
|
12
|
+
linkml_store/api/stores/duckdb/__init__.py,sha256=qZExreYUUQmKDZKN-fiLH1DfTYWojUkmkcT2LzrdMH8,213
|
|
13
|
+
linkml_store/api/stores/duckdb/duckdb_collection.py,sha256=WLo9q67pRQAhz6ErAEAxTRAGOe77FQ0meHi2schgAeo,5854
|
|
14
|
+
linkml_store/api/stores/duckdb/duckdb_database.py,sha256=99jx3B0498Glqbm5QvfK3sIbluN-wzJ8Kqc0teh2MM4,6663
|
|
15
|
+
linkml_store/api/stores/duckdb/mappings.py,sha256=S4MWetLpQcxOwwedXrZTqazxdaHIQXXbq4VRq9Ok4B4,123
|
|
16
|
+
linkml_store/api/stores/hdf5/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
linkml_store/api/stores/hdf5/hdf5_collection.py,sha256=mnpLMYehn3PuaIjp2dXrIWu8jh-bdQ84X2Ku83jMdEY,3805
|
|
18
|
+
linkml_store/api/stores/hdf5/hdf5_database.py,sha256=EZbjrpaqiNDEFvoD5dZNcGBXA8z6HRNL81emueTZWNw,2714
|
|
19
|
+
linkml_store/api/stores/mongodb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
linkml_store/api/stores/mongodb/mongodb_collection.py,sha256=sQZa59A0NsW13k6kKPSyo8FZR_h7ebm6FMXfu1NAvYs,4111
|
|
21
|
+
linkml_store/api/stores/mongodb/mongodb_database.py,sha256=QAdTi8XYLsdrEvEUUKb9qolCPeEXgfecTQ1bz9GCWDg,3670
|
|
22
|
+
linkml_store/api/stores/solr/__init__.py,sha256=aAfnaN9mZOiIDj1NYz0Ll9fZF2gG7UU_vhP4SNCL2d8,36
|
|
23
|
+
linkml_store/api/stores/solr/solr_collection.py,sha256=8GmxErlWFOO0NnJiYo1Q7hegxCsfxaWc79eN7Jn02gA,4723
|
|
24
|
+
linkml_store/api/stores/solr/solr_database.py,sha256=TFjqbY7jAkdrhAchbNg0E-mChSP7ogNwFExslbvX7Yo,2877
|
|
25
|
+
linkml_store/api/stores/solr/solr_utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
+
linkml_store/cli.py,sha256=r6UggDS2iFC5yIhBECwn-5aKmI38SHuYIJ5TEA0-CeM,13667
|
|
27
|
+
linkml_store/constants.py,sha256=x4ZmDsfE9rZcL5WpA93uTKrRWzCD6GodYXviVzIvR38,112
|
|
28
|
+
linkml_store/index/__init__.py,sha256=k3fq2gzhoBv3_QEu4zMbEEoc0tEOUOcrEjKQVvWfASs,881
|
|
29
|
+
linkml_store/index/implementations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
+
linkml_store/index/implementations/llm_indexer.py,sha256=LymhrQIeik0Qe8P2b3y2Lg7Y0P0OJ8_WjFwv-hbULj8,1069
|
|
31
|
+
linkml_store/index/implementations/simple_indexer.py,sha256=CwUBb_GO_JUd-f-KW6R4i26PUV251BNTZ2cE6Qo1fH4,1251
|
|
32
|
+
linkml_store/index/indexer.py,sha256=d_QEwJ5qEx2pkNR-QJ9hAn0pyJbhZIIT83GBFKhJA6I,3462
|
|
33
|
+
linkml_store/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
+
linkml_store/utils/format_utils.py,sha256=lTrNVEywRziCjcrEeUC3hpZZo8yDQYgG-qBanQhBYrI,3081
|
|
35
|
+
linkml_store/utils/io.py,sha256=JHUrWDtlZC2jtN_PQZ4ypdGIyYlftZEN3JaCvEPs44w,884
|
|
36
|
+
linkml_store/utils/object_utils.py,sha256=is6T2gruvVKvWD5ZntcAl6Qi3L154FObEho_b_crTuE,2539
|
|
37
|
+
linkml_store/utils/sql_utils.py,sha256=TeAhAHXi1GA0f2UVrxbzStwe49Q7fN0mu5WZyfDk-s8,5651
|
|
38
|
+
linkml_store-0.1.7.dist-info/LICENSE,sha256=77mDOslUnalYnuq9xQYZKtIoNEzcH9mIjvWHOKjamnE,1086
|
|
39
|
+
linkml_store-0.1.7.dist-info/METADATA,sha256=aby4QxnuE6G00g38tjZXH4B4-U7qfJKGtJtIx0OH5ew,2064
|
|
40
|
+
linkml_store-0.1.7.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
41
|
+
linkml_store-0.1.7.dist-info/entry_points.txt,sha256=6ema3OkAkUK0ux8roEeRPtSW_Tylend5BABf-xRsZiU,53
|
|
42
|
+
linkml_store-0.1.7.dist-info/RECORD,,
|
linkml_store/api/metadata.py
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
linkml_store/__init__.py,sha256=jlU6WOUAn8cKIhzbTULmBTWpW9gZdEt7q_RI6KZN1bY,118
|
|
2
|
-
linkml_store/api/__init__.py,sha256=Il3wGkN6evgc3OOWQYL7q3J-tRBs_0yQ3Nj-s6eo-0Y,284
|
|
3
|
-
linkml_store/api/client.py,sha256=6zGzhTd6zJ-V5fzLbgljI6onq_XVvaxoeyXK5d1U9wM,4353
|
|
4
|
-
linkml_store/api/collection.py,sha256=zr9iMXCJPsBOYLwO8I0dLIl01sVG4sgXtpgVdHJIVNo,11334
|
|
5
|
-
linkml_store/api/database.py,sha256=oulUxyUGfq6dMQge___dYcevunDOIlLUBGTeewRZqDs,6682
|
|
6
|
-
linkml_store/api/metadata.py,sha256=k9F6D_nuIZ0wWocj0ew2FYSKOc06CJWvwoUpHcrs7JA,69
|
|
7
|
-
linkml_store/api/queries.py,sha256=5WgI_od_Qlpiza-u-XCrxL0F3Etf6mdeCCSSxeHT0PI,1698
|
|
8
|
-
linkml_store/api/stores/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
linkml_store/api/stores/duckdb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
linkml_store/api/stores/duckdb/duckdb_collection.py,sha256=3hxwnRoY8pIZuW09yi4BSRDUT_pzTo9Mj82SBTT1aNw,4194
|
|
11
|
-
linkml_store/api/stores/duckdb/duckdb_database.py,sha256=efTwzAoAc4YugBqNl9aQhlBzcPU4GDLwGQC5UPk_Uuw,6104
|
|
12
|
-
linkml_store/api/stores/duckdb/mappings.py,sha256=S4MWetLpQcxOwwedXrZTqazxdaHIQXXbq4VRq9Ok4B4,123
|
|
13
|
-
linkml_store/api/stores/mongodb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
linkml_store/api/stores/mongodb/mongodb_collection.py,sha256=atFpJWTENwCBmRjtC5iWkwwV-3XnVr16A_nuXDS76Ig,2094
|
|
15
|
-
linkml_store/api/stores/mongodb/mongodb_database.py,sha256=EpiTUftXlmOC8V4lPcrGOX2xq0leqWpmPVaZGsUMESI,3984
|
|
16
|
-
linkml_store/constants.py,sha256=x4ZmDsfE9rZcL5WpA93uTKrRWzCD6GodYXviVzIvR38,112
|
|
17
|
-
linkml_store/index/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
linkml_store/index/implementations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
-
linkml_store/index/implementations/llm_index.py,sha256=JAxg1dIuU7W8ZtnsGcogbBUvyrPTZCbKhEFv-0lEKrk,1061
|
|
20
|
-
linkml_store/index/implementations/simple_index.py,sha256=ep73dg86QqV7B7t_VoOK1weVx5RD0xvJ7uJPsszd5I0,1134
|
|
21
|
-
linkml_store/index/index.py,sha256=uwgKtw76ch0qe5CbSj2Ft_WAhY94Qm5qLBJucl-VPWA,3420
|
|
22
|
-
linkml_store/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
-
linkml_store/utils/io.py,sha256=JHUrWDtlZC2jtN_PQZ4ypdGIyYlftZEN3JaCvEPs44w,884
|
|
24
|
-
linkml_store/utils/sql_utils.py,sha256=sCM9GqEW6zZ3-2n5tsW7gzi0DerP6TXx3AJDY_0kCJ4,4557
|
|
25
|
-
linkml_store-0.0.0.dist-info/LICENSE,sha256=77mDOslUnalYnuq9xQYZKtIoNEzcH9mIjvWHOKjamnE,1086
|
|
26
|
-
linkml_store-0.0.0.dist-info/METADATA,sha256=Ignb5ab4IwjIfm60g2yciUqYBb0IstFvABlOMFTygPk,1717
|
|
27
|
-
linkml_store-0.0.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
28
|
-
linkml_store-0.0.0.dist-info/entry_points.txt,sha256=YdXo7B96u7fP8WX9cirGelqH0kjx7vGjT-w8hq8HFSE,54
|
|
29
|
-
linkml_store-0.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|