Path: blob/main/singlestoredb/fusion/handlers/files.py
469 views
#!/usr/bin/env python31from typing import Any2from typing import Dict3from typing import Optional45from .. import result6from ..handler import SQLHandler7from ..result import FusionSQLResult8from .utils import dt_isoformat9from .utils import get_file_space101112class ShowFilesHandler(SQLHandler):13"""14Generic handler for listing files in a personal/shared space.15""" # noqa: E5011617def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:18file_space = get_file_space(params)1920res = FusionSQLResult()21res.add_field('Name', result.STRING)2223if params['extended']:24res.add_field('Type', result.STRING)25res.add_field('Size', result.INTEGER)26res.add_field('Writable', result.STRING)27res.add_field('CreatedAt', result.DATETIME)28res.add_field('LastModifiedAt', result.DATETIME)2930files = []31for x in file_space.listdir(32params['at_path'] or '/',33recursive=params['recursive'],34):35info = file_space.info(x)36files.append(37tuple([38x, info.type, info.size or 0, info.writable,39dt_isoformat(info.created_at),40dt_isoformat(info.last_modified_at),41]),42)43res.set_rows(files)4445else:46res.set_rows([(x,) for x in file_space.listdir(47params['at_path'] or '/',48recursive=params['recursive'],49)])5051if params['like']:52res = res.like(Name=params['like'])5354return res.order_by(**params['order_by']).limit(params['limit'])555657class ShowPersonalFilesHandler(ShowFilesHandler):58"""59SHOW PERSONAL FILES60[ at_path ] [ <like> ]61[ <order-by> ]62[ <limit> ] [ recursive ] [ extended ];6364# File path to list65at_path = AT '<path>'6667# Should the listing be recursive?68recursive = RECURSIVE6970# Should extended attributes be shown?71extended = EXTENDED7273Description74-----------75Displays a list of files in a personal/shared space.7677Arguments78---------79* ``<path>``: A path in the personal/shared space.80* ``<pattern>``: A pattern similar to SQL LIKE clause.81Uses ``%`` as the wildcard character.8283Remarks84-------85* Use the ``LIKE`` clause to specify a pattern and return only the86files that match the specified pattern.87* The ``LIMIT`` clause limits the number of results to the88specified number.89* Use the ``ORDER BY`` clause to sort the results by the specified90key. By default, the results are sorted in the ascending order.91* The ``AT`` clause specifies the path in the personal/shared92space to list the files from.93* Use the ``RECURSIVE`` clause to list the files recursively.94* To return more information about the files, use the ``EXTENDED``95clause.9697Examples98--------99The following command lists the files at a specific path::100101SHOW PERSONAL FILES AT "/data/";102103The following command lists the files recursively with104additional information::105106SHOW PERSONAL FILES RECURSIVE EXTENDED;107108See Also109--------110* ``SHOW SHARED FILES``111* ``UPLOAD PERSONAL FILE``112* ``UPLOAD SHARED FILE``113* ``DOWNLOAD PERSONAL FILE``114* ``DOWNLOAD SHARED FILE``115116""" # noqa: E501117def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:118params['file_location'] = 'PERSONAL'119return super().run(params)120121122class ShowSharedFilesHandler(ShowFilesHandler):123"""124SHOW SHARED FILES125[ at_path ] [ <like> ]126[ <order-by> ]127[ <limit> ] [ recursive ] [ extended ];128129# File path to list130at_path = AT '<path>'131132# Should the listing be recursive?133recursive = RECURSIVE134135# Should extended attributes be shown?136extended = EXTENDED137138Description139-----------140Displays a list of files in a personal/shared space.141142Arguments143---------144* ``<path>``: A path in the personal/shared space.145* ``<pattern>``: A pattern similar to SQL LIKE clause.146Uses ``%`` as the wildcard character.147148Remarks149-------150* Use the ``LIKE`` clause to specify a pattern and return only the151files that match the specified pattern.152* The ``LIMIT`` clause limits the number of results to the153specified number.154* Use the ``ORDER BY`` clause to sort the results by the specified155key. By default, the results are sorted in the ascending order.156* The ``AT`` clause specifies the path in the personal/shared157space to list the files from.158* Use the ``RECURSIVE`` clause to list the files recursively.159* To return more information about the files, use the ``EXTENDED``160clause.161162Examples163--------164The following command lists the files at a specific path::165166SHOW SHARED FILES AT "/data/";167168The following command lists the files recursively with169additional information::170171SHOW SHARED FILES RECURSIVE EXTENDED;172173See Also174--------175* ``SHOW PERSONAL FILES``176* ``UPLOAD PERSONAL FILE``177* ``UPLOAD SHARED FILE``178* ``DOWNLOAD PERSONAL FILE``179* ``DOWNLOAD SHARED FILE``180181""" # noqa: E501182def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:183params['file_location'] = 'SHARED'184return super().run(params)185186187ShowPersonalFilesHandler.register(overwrite=True)188ShowSharedFilesHandler.register(overwrite=True)189190191class UploadFileHandler(SQLHandler):192"""193Generic handler for uploading files to a personal/shared space.194""" # noqa: E501195196def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:197file_space = get_file_space(params)198file_space.upload_file(199params['local_path'], params['path'],200overwrite=params['overwrite'],201)202return None203204205class UploadPersonalFileHandler(UploadFileHandler):206"""207UPLOAD PERSONAL FILE TO path208FROM local_path [ overwrite ];209210# Path to file211path = '<filename>'212213# Path to local file214local_path = '<local-path>'215216# Should an existing file be overwritten?217overwrite = OVERWRITE218219Description220-----------221Uploads a file to a personal/shared space.222223Arguments224---------225* ``<filename>``: The filename in the personal/shared space where the file is uploaded.226* ``<local-path>``: The path to the file to upload in the local227directory.228229Remarks230-------231* If the ``OVERWRITE`` clause is specified, any existing file at the232specified path in the personal/shared space is overwritten.233234Examples235--------236The following command uploads a file to a personal/shared space and overwrite any237existing files at the specified path::238239UPLOAD PERSONAL FILE TO 'stats.csv'240FROM '/tmp/user/stats.csv' OVERWRITE;241242See Also243--------244* ``UPLOAD SHARED FILE``245* ``DOWNLOAD PERSONAL FILE``246* ``DOWNLOAD SHARED FILE``247248""" # noqa: E501249250def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:251params['file_location'] = 'PERSONAL'252return super().run(params)253254255class UploadSharedFileHandler(UploadFileHandler):256"""257UPLOAD SHARED FILE TO path258FROM local_path [ overwrite ];259260# Path to file261path = '<filename>'262263# Path to local file264local_path = '<local-path>'265266# Should an existing file be overwritten?267overwrite = OVERWRITE268269Description270-----------271Uploads a file to a personal/shared space.272273Arguments274---------275* ``<filename>``: The filename in the personal/shared space where the file is uploaded.276* ``<local-path>``: The path to the file to upload in the local277directory.278279Remarks280-------281* If the ``OVERWRITE`` clause is specified, any existing file at the282specified path in the personal/shared space is overwritten.283284Examples285--------286The following command uploads a file to a personal/shared space and overwrite any287existing files at the specified path::288289UPLOAD SHARED FILE TO 'stats.csv'290FROM '/tmp/user/stats.csv' OVERWRITE;291292See Also293--------294* ``UPLOAD PERSONAL FILE``295* ``DOWNLOAD PERSONAL FILE``296* ``DOWNLOAD SHARED FILE``297298""" # noqa: E501299300def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:301params['file_location'] = 'SHARED'302return super().run(params)303304305UploadPersonalFileHandler.register(overwrite=True)306UploadSharedFileHandler.register(overwrite=True)307308309class DownloadFileHandler(SQLHandler):310"""311Generic handler for downloading files from a personal/shared space.312""" # noqa: E501313314def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:315file_space = get_file_space(params)316317out = file_space.download_file(318params['path'],319local_path=params['local_path'] or None,320overwrite=params['overwrite'],321encoding=params['encoding'] or None,322)323324if not params['local_path']:325res = FusionSQLResult()326if params['encoding']:327res.add_field('Data', result.STRING)328else:329res.add_field('Data', result.BLOB)330res.set_rows([(out,)])331return res332333return None334335336class DownloadPersonalFileHandler(DownloadFileHandler):337"""338DOWNLOAD PERSONAL FILE path339[ local_path ]340[ overwrite ]341[ encoding ];342343# Path to file344path = '<path>'345346# Path to local file347local_path = TO '<local-path>'348349# Should an existing file be overwritten?350overwrite = OVERWRITE351352# File encoding353encoding = ENCODING '<encoding>'354355Description356-----------357Download a file from a personal/shared space.358359Arguments360---------361* ``<path>``: The path to the file to download in a personal/shared space.362* ``<encoding>``: The encoding to apply to the downloaded file.363* ``<local-path>``: Specifies the path in the local directory364where the file is downloaded.365366Remarks367-------368* If the ``OVERWRITE`` clause is specified, any existing file at369the download location is overwritten.370* By default, files are downloaded in binary encoding. To view371the contents of the file on the standard output, use the372``ENCODING`` clause and specify an encoding.373* If ``<local-path>`` is not specified, the file is displayed374on the standard output.375376Examples377--------378The following command displays the contents of the file on the379standard output::380381DOWNLOAD PERSONAL FILE '/data/stats.csv' ENCODING 'utf8';382383The following command downloads a file to a specific location and384overwrites any existing file with the name ``stats.csv`` on the local storage::385386DOWNLOAD PERSONAL FILE '/data/stats.csv'387TO '/tmp/data.csv' OVERWRITE;388389See Also390--------391* ``DOWNLOAD SHARED FILE``392* ``UPLOAD PERSONAL FILE``393* ``UPLOAD SHARED FILE``394395""" # noqa: E501396397def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:398params['file_location'] = 'PERSONAL'399return super().run(params)400401402class DownloadSharedFileHandler(DownloadFileHandler):403"""404DOWNLOAD SHARED FILE path405[ local_path ]406[ overwrite ]407[ encoding ];408409# Path to file410path = '<path>'411412# Path to local file413local_path = TO '<local-path>'414415# Should an existing file be overwritten?416overwrite = OVERWRITE417418# File encoding419encoding = ENCODING '<encoding>'420421Description422-----------423Download a file from a personal/shared space.424425Arguments426---------427* ``<path>``: The path to the file to download in a personal/shared space.428* ``<encoding>``: The encoding to apply to the downloaded file.429* ``<local-path>``: Specifies the path in the local directory430where the file is downloaded.431432Remarks433-------434* If the ``OVERWRITE`` clause is specified, any existing file at435the download location is overwritten.436* By default, files are downloaded in binary encoding. To view437the contents of the file on the standard output, use the438``ENCODING`` clause and specify an encoding.439* If ``<local-path>`` is not specified, the file is displayed440on the standard output.441442Examples443--------444The following command displays the contents of the file on the445standard output::446447DOWNLOAD SHARED FILE '/data/stats.csv' ENCODING 'utf8';448449The following command downloads a file to a specific location and450overwrites any existing file with the name ``stats.csv`` on the local storage::451452DOWNLOAD SHARED FILE '/data/stats.csv'453TO '/tmp/data.csv' OVERWRITE;454455See Also456--------457* ``DOWNLOAD PERSONAL FILE``458* ``UPLOAD PERSONAL FILE``459* ``UPLOAD SHARED FILE``460461""" # noqa: E501462463def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:464params['file_location'] = 'SHARED'465return super().run(params)466467468DownloadPersonalFileHandler.register(overwrite=True)469DownloadSharedFileHandler.register(overwrite=True)470471472class DropHandler(SQLHandler):473"""474Generic handler for deleting files/folders from a personal/shared space.475""" # noqa: E501476477def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:478file_space = get_file_space(params)479480file_type = params['file_type']481if not file_type:482raise KeyError('file type was not specified')483484file_type = file_type.lower()485if file_type not in ['file', 'folder']:486raise ValueError('file type must be either FILE or FOLDER')487488if file_type == 'file':489file_space.remove(params['path'])490elif file_type == 'folder':491if params['recursive']:492file_space.removedirs(params['path'])493else:494file_space.rmdir(params['path'])495496return None497498499class DropPersonalHandler(DropHandler):500"""501DROP PERSONAL <file-type> path502[ recursive ];503504# Path to file505path = '<path>'506507# Should folders be deleted recursively?508recursive = RECURSIVE509510Description511-----------512Deletes a file/folder from a personal/shared space.513514Arguments515---------516* ``<file-type>``: The type of the file, it can517be either 'FILE' or 'FOLDER'.518* ``<path>``: The path to the file to delete in a personal/shared space.519520Remarks521-------522* The ``RECURSIVE`` clause indicates that the specified folder523is deleted recursively.524525Example526--------527The following commands delete a file/folder from a personal/shared space::528529DROP PERSONAL FILE '/data/stats.csv';530DROP PERSONAL FOLDER '/data/' RECURSIVE;531532See Also533--------534* ``DROP SHARED FILE``535* ``DROP SHARED FOLDER``536537""" # noqa: E501538539def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:540params['file_location'] = 'PERSONAL'541return super().run(params)542543544class DropSharedHandler(DropHandler):545"""546DROP SHARED <file-type> path547[ recursive ];548549# Path to file550path = '<path>'551552# Should folders be deleted recursively?553recursive = RECURSIVE554555Description556-----------557Deletes a file/folder from a personal/shared space.558559Arguments560---------561* ``<file-type>``: The type of the file, it can562be either 'FILE' or 'FOLDER'.563* ``<path>``: The path to the file to delete in a personal/shared space.564565Remarks566-------567* The ``RECURSIVE`` clause indicates that the specified folder568is deleted recursively.569570Example571--------572The following commands delete a file/folder from a personal/shared space::573574DROP SHARED FILE '/data/stats.csv';575DROP SHARED FOLDER '/data/' RECURSIVE;576577See Also578--------579* ``DROP PERSONAL FILE``580* ``DROP PERSONAL FOLDER``581582""" # noqa: E501583584def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:585params['file_location'] = 'SHARED'586return super().run(params)587588589DropPersonalHandler.register(overwrite=True)590DropSharedHandler.register(overwrite=True)591592593class CreateFolderHandler(SQLHandler):594"""595Generic handler for creating folders in a personal/shared space.596"""597598def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:599file_space = get_file_space(params)600file_space.mkdir(params['path'], overwrite=params['overwrite'])601return None602603604class CreatePersonalFolderHandler(CreateFolderHandler):605"""606CREATE PERSONAL FOLDER path607[ overwrite ];608609# Path to folder610path = '<path>'611612# Should an existing folder be overwritten?613overwrite = OVERWRITE614615Description616-----------617Creates a new folder at the specified path in a personal/shared space.618619Arguments620---------621* ``<path>``: The path in a personal/shared space where the folder622is created. The path must end with a trailing slash (/).623624Remarks625-------626* If the ``OVERWRITE`` clause is specified, any existing627folder at the specified path is overwritten.628629Example630-------631The following command creates a folder in a personal/shared space::632633CREATE PERSONAL FOLDER `/data/csv/`;634635See Also636--------637* ``CREATE SHARED FOLDER``638639"""640641def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:642params['file_location'] = 'PERSONAL'643return super().run(params)644645646class CreateSharedFolderHandler(CreateFolderHandler):647"""648CREATE SHARED FOLDER path649[ overwrite ];650651# Path to folder652path = '<path>'653654# Should an existing folder be overwritten?655overwrite = OVERWRITE656657Description658-----------659Creates a new folder at the specified path in a personal/shared space.660661Arguments662---------663* ``<path>``: The path in a personal/shared space where the folder664is created. The path must end with a trailing slash (/).665666Remarks667-------668* If the ``OVERWRITE`` clause is specified, any existing669folder at the specified path is overwritten.670671Example672-------673The following command creates a folder in a personal/shared space::674675CREATE SHARED FOLDER `/data/csv/`;676677See Also678--------679* ``CREATE PERSONAL FOLDER``680681"""682683def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:684params['file_location'] = 'SHARED'685return super().run(params)686687688CreatePersonalFolderHandler.register(overwrite=True)689CreateSharedFolderHandler.register(overwrite=True)690691692