-- Create tables for different large object storage methodsCREATETABLEdocuments(idSERIALPRIMARYKEY,filenameVARCHAR(255)NOTNULL,content_typeVARCHAR(100)NOTNULL,file_sizeBIGINTNOTNULL,contentBYTEA,-- BYTEA for smaller files (< 1GB)metadataJSONB,created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP,updated_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);CREATETABLElarge_files(idSERIALPRIMARYKEY,filenameVARCHAR(255)NOTNULL,content_typeVARCHAR(100)NOTNULL,file_sizeBIGINTNOTNULL,oidOID,-- Large Object OID for larger filesmetadataJSONB,created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP,updated_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);CREATETABLEfile_references(idSERIALPRIMARYKEY,filenameVARCHAR(255)NOTNULL,content_typeVARCHAR(100)NOTNULL,file_sizeBIGINTNOTNULL,storage_pathVARCHAR(500)NOTNULL,-- External storage pathchecksumVARCHAR(64)NOTNULL,-- File integrity checksummetadataJSONB,created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP,updated_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);
-- Create function to store large objectCREATEORREPLACEFUNCTIONstore_large_object(p_filenameVARCHAR(255),p_content_typeVARCHAR(100),p_file_dataBYTEA)RETURNSINTEGERAS$$DECLAREfile_oidOID;file_sizeBIGINT;BEGIN-- Create large objectfile_oid:=lo_create(0);-- Open large object for writingPERFORMlo_open(file_oid,131072);-- 131072 = INV_WRITE-- Write data to large objectPERFORMlo_write(file_oid,0,p_file_data);-- Close large objectPERFORMlo_close(file_oid);-- Get file sizefile_size:=LENGTH(p_file_data);-- Insert recordINSERTINTOlarge_files(filename,content_type,file_size,oid)VALUES(p_filename,p_content_type,file_size,file_oid);RETURNfile_oid;END;$$LANGUAGEplpgsql;-- Create function to retrieve large objectCREATEORREPLACEFUNCTIONretrieve_large_object(p_oidOID)RETURNSBYTEAAS$$DECLAREfile_dataBYTEA;file_sizeBIGINT;BEGIN-- Get file sizeSELECTfile_sizeINTOfile_sizeFROMlarge_filesWHEREoid=p_oid;-- Open large object for readingPERFORMlo_open(p_oid,262144);-- 262144 = INV_READ-- Read data from large objectSELECTlo_read(p_oid,0,file_size)INTOfile_data;-- Close large objectPERFORMlo_close(p_oid);RETURNfile_data;END;$$LANGUAGEplpgsql;
-- Create function for file uploadCREATEORREPLACEFUNCTIONupload_file(p_filenameVARCHAR(255),p_content_typeVARCHAR(100),p_file_dataBYTEA,p_metadataJSONBDEFAULT'{}')RETURNSINTEGERAS$$DECLAREfile_idINTEGER;file_sizeBIGINT;file_checksumVARCHAR(64);BEGIN-- Calculate file size and checksumfile_size:=LENGTH(p_file_data);file_checksum:=encode(digest(p_file_data,'sha256'),'hex');-- Check if file already existsIFEXISTS(SELECT1FROMfile_referencesWHEREchecksum=file_checksum)THENRAISEEXCEPTION'File already exists with checksum: %',file_checksum;ENDIF;-- Store file based on sizeIFfile_size<1048576THEN-- 1MB-- Store small files in BYTEAINSERTINTOdocuments(filename,content_type,file_size,content,metadata)VALUES(p_filename,p_content_type,file_size,p_file_data,p_metadata)RETURNINGidINTOfile_id;ELSE-- Store large files as Large ObjectsINSERTINTOlarge_files(filename,content_type,file_size,oid,metadata)VALUES(p_filename,p_content_type,file_size,store_large_object(p_filename,p_content_type,p_file_data),p_metadata)RETURNINGidINTOfile_id;ENDIF;RETURNfile_id;END;$$LANGUAGEplpgsql;-- Create function for file downloadCREATEORREPLACEFUNCTIONdownload_file(p_file_idINTEGER)RETURNSTABLE(filenameVARCHAR(255),content_typeVARCHAR(100),file_sizeBIGINT,file_dataBYTEA,metadataJSONB)AS$$BEGINRETURNQUERYSELECTd.filename,d.content_type,d.file_size,d.contentasfile_data,d.metadataFROMdocumentsdWHEREd.id=p_file_idUNIONALLSELECTlf.filename,lf.content_type,lf.file_size,retrieve_large_object(lf.oid)asfile_data,lf.metadataFROMlarge_fileslfWHERElf.id=p_file_id;END;$$LANGUAGEplpgsql;
-- Create function to validate file integrityCREATEORREPLACEFUNCTIONvalidate_file_integrity(p_file_idINTEGER)RETURNSBOOLEANAS$$DECLAREstored_checksumVARCHAR(64);calculated_checksumVARCHAR(64);file_dataBYTEA;BEGIN-- Get stored checksum and file dataSELECTchecksum,contentINTOstored_checksum,file_dataFROMdocumentsWHEREid=p_file_id;IFstored_checksumISNULLTHEN-- Check large files tableSELECTchecksum,retrieve_large_object(oid)INTOstored_checksum,file_dataFROMlarge_filesWHEREid=p_file_id;ENDIF;IFstored_checksumISNULLTHENRETURNFALSE;ENDIF;-- Calculate current checksumcalculated_checksum:=encode(digest(file_data,'sha256'),'hex');-- Compare checksumsRETURNstored_checksum=calculated_checksum;END;$$LANGUAGEplpgsql;-- Create function to update file checksumsCREATEORREPLACEFUNCTIONupdate_file_checksums()RETURNSINTEGERAS$$DECLAREfile_recordRECORD;calculated_checksumVARCHAR(64);updated_countINTEGER:=0;BEGIN-- Update checksums for documentsFORfile_recordINSELECTid,contentFROMdocumentsWHEREchecksumISNULLLOOPcalculated_checksum:=encode(digest(file_record.content,'sha256'),'hex');UPDATEdocumentsSETchecksum=calculated_checksumWHEREid=file_record.id;updated_count:=updated_count+1;ENDLOOP;-- Update checksums for large filesFORfile_recordINSELECTid,oidFROMlarge_filesWHEREchecksumISNULLLOOPcalculated_checksum:=encode(digest(retrieve_large_object(file_record.oid),'sha256'),'hex');UPDATElarge_filesSETchecksum=calculated_checksumWHEREid=file_record.id;updated_count:=updated_count+1;ENDLOOP;RETURNupdated_count;END;$$LANGUAGEplpgsql;
-- Create function for S3 storage integrationCREATEORREPLACEFUNCTIONstore_file_s3(p_filenameVARCHAR(255),p_content_typeVARCHAR(100),p_file_dataBYTEA,p_bucket_nameVARCHAR(100),p_metadataJSONBDEFAULT'{}')RETURNSINTEGERAS$$DECLAREfile_idINTEGER;file_sizeBIGINT;file_checksumVARCHAR(64);storage_pathVARCHAR(500);BEGIN-- Calculate file size and checksumfile_size:=LENGTH(p_file_data);file_checksum:=encode(digest(p_file_data,'sha256'),'hex');-- Generate storage pathstorage_path:=p_bucket_name||'/'||to_char(CURRENT_DATE,'YYYY/MM/DD')||'/'||file_checksum||'_'||p_filename;-- Store file referenceINSERTINTOfile_references(filename,content_type,file_size,storage_path,checksum,metadata)VALUES(p_filename,p_content_type,file_size,storage_path,file_checksum,p_metadata)RETURNINGidINTOfile_id;-- Note: Actual S3 upload would be handled by application code-- This function only creates the database recordRETURNfile_id;END;$$LANGUAGEplpgsql;-- Create function to retrieve file from S3CREATEORREPLACEFUNCTIONretrieve_file_s3(p_file_idINTEGER)RETURNSTABLE(filenameVARCHAR(255),content_typeVARCHAR(100),file_sizeBIGINT,storage_pathVARCHAR(500),checksumVARCHAR(64),metadataJSONB)AS$$BEGINRETURNQUERYSELECTfr.filename,fr.content_type,fr.file_size,fr.storage_path,fr.checksum,fr.metadataFROMfile_referencesfrWHEREfr.id=p_file_id;END;$$LANGUAGEplpgsql;
-- Create function for file cleanupCREATEORREPLACEFUNCTIONcleanup_orphaned_files()RETURNSINTEGERAS$$DECLAREorphaned_countINTEGER:=0;file_recordRECORD;BEGIN-- Find orphaned large objectsFORfile_recordINSELECToidFROMlarge_filesWHERENOTEXISTS(SELECT1FROMpg_largeobject_metadataWHEREoid=large_files.oid)LOOP-- Delete orphaned large objectPERFORMlo_unlink(file_record.oid);orphaned_count:=orphaned_count+1;ENDLOOP;-- Delete orphaned file referencesDELETEFROMfile_referencesWHEREstorage_pathISNOTNULLANDNOTEXISTS(SELECT1FROMfile_referencesfr2WHEREfr2.storage_path=file_references.storage_path);GETDIAGNOSTICSorphaned_count=orphaned_count+ROW_COUNT;RETURNorphaned_count;END;$$LANGUAGEplpgsql;-- Create function for file size analysisCREATEORREPLACEFUNCTIONanalyze_file_storage()RETURNSTABLE(storage_typeTEXT,file_countBIGINT,total_sizeBIGINT,avg_sizeNUMERIC,max_sizeBIGINT,min_sizeBIGINT)AS$$BEGINRETURNQUERYSELECT'BYTEA'::TEXTasstorage_type,COUNT(*)asfile_count,SUM(file_size)astotal_size,AVG(file_size)asavg_size,MAX(file_size)asmax_size,MIN(file_size)asmin_sizeFROMdocumentsUNIONALLSELECT'Large Objects'::TEXTasstorage_type,COUNT(*)asfile_count,SUM(file_size)astotal_size,AVG(file_size)asavg_size,MAX(file_size)asmax_size,MIN(file_size)asmin_sizeFROMlarge_filesUNIONALLSELECT'External Storage'::TEXTasstorage_type,COUNT(*)asfile_count,SUM(file_size)astotal_size,AVG(file_size)asavg_size,MAX(file_size)asmax_size,MIN(file_size)asmin_sizeFROMfile_references;END;$$LANGUAGEplpgsql;
-- Create function for file access controlCREATEORREPLACEFUNCTIONcheck_file_access(p_file_idINTEGER,p_user_idINTEGER,p_access_typeVARCHAR(20)DEFAULT'read')RETURNSBOOLEANAS$$DECLAREfile_ownerINTEGER;file_permissionsJSONB;BEGIN-- Get file owner and permissionsSELECTowner_id,permissionsINTOfile_owner,file_permissionsFROM(SELECTid,owner_id,permissionsFROMdocumentsWHEREid=p_file_idUNIONALLSELECTid,owner_id,permissionsFROMlarge_filesWHEREid=p_file_idUNIONALLSELECTid,owner_id,permissionsFROMfile_referencesWHEREid=p_file_id)t;-- Check if user is ownerIFfile_owner=p_user_idTHENRETURNTRUE;ENDIF;-- Check permissionsIFfile_permissionsISNOTNULLTHENIFp_access_type='read'AND(file_permissions?'read'ORfile_permissions?'write')THENRETURNTRUE;ENDIF;IFp_access_type='write'ANDfile_permissions?'write'THENRETURNTRUE;ENDIF;ENDIF;RETURNFALSE;END;$$LANGUAGEplpgsql;-- Create function for secure file retrievalCREATEORREPLACEFUNCTIONsecure_download_file(p_file_idINTEGER,p_user_idINTEGER)RETURNSTABLE(filenameVARCHAR(255),content_typeVARCHAR(100),file_sizeBIGINT,file_dataBYTEA,metadataJSONB)AS$$BEGIN-- Check access permissionsIFNOTcheck_file_access(p_file_id,p_user_id,'read')THENRAISEEXCEPTION'Access denied for file %',p_file_id;ENDIF;-- Return file dataRETURNQUERYSELECT*FROMdownload_file(p_file_id);END;$$LANGUAGEplpgsql;
-- Create function for file encryptionCREATEORREPLACEFUNCTIONencrypt_file_data(p_file_dataBYTEA,p_encryption_keyTEXT)RETURNSBYTEAAS$$BEGIN-- Use pgcrypto for encryptionRETURNpgp_sym_encrypt(p_file_data,p_encryption_key);END;$$LANGUAGEplpgsql;-- Create function for file decryptionCREATEORREPLACEFUNCTIONdecrypt_file_data(p_encrypted_dataBYTEA,p_encryption_keyTEXT)RETURNSBYTEAAS$$BEGIN-- Use pgcrypto for decryptionRETURNpgp_sym_decrypt(p_encrypted_data,p_encryption_key);END;$$LANGUAGEplpgsql;-- Create function for secure file storageCREATEORREPLACEFUNCTIONstore_encrypted_file(p_filenameVARCHAR(255),p_content_typeVARCHAR(100),p_file_dataBYTEA,p_encryption_keyTEXT,p_metadataJSONBDEFAULT'{}')RETURNSINTEGERAS$$DECLAREfile_idINTEGER;encrypted_dataBYTEA;file_sizeBIGINT;file_checksumVARCHAR(64);BEGIN-- Encrypt file dataencrypted_data:=encrypt_file_data(p_file_data,p_encryption_key);-- Calculate file size and checksumfile_size:=LENGTH(p_file_data);file_checksum:=encode(digest(p_file_data,'sha256'),'hex');-- Store encrypted fileINSERTINTOdocuments(filename,content_type,file_size,content,metadata)VALUES(p_filename,p_content_type,file_size,encrypted_data,p_metadata)RETURNINGidINTOfile_id;RETURNfile_id;END;$$LANGUAGEplpgsql;
# monitoring/large_object_monitor.pyimportpsycopg2importjsonfromdatetimeimportdatetimeimportloggingclassLargeObjectMonitor:def__init__(self,connection_params):self.conn_params=connection_paramsself.setup_logging()defsetup_logging(self):"""Setup logging."""logging.basicConfig(level=logging.INFO)self.logger=logging.getLogger(__name__)defget_storage_statistics(self):"""Get storage statistics."""conn=psycopg2.connect(**self.conn_params)try:withconn.cursor()ascur:cur.execute(""" SELECT 'documents' as table_name, COUNT(*) as file_count, SUM(file_size) as total_size, AVG(file_size) as avg_size, MAX(file_size) as max_size, MIN(file_size) as min_size FROM documents UNION ALL SELECT 'large_files' as table_name, COUNT(*) as file_count, SUM(file_size) as total_size, AVG(file_size) as avg_size, MAX(file_size) as max_size, MIN(file_size) as min_size FROM large_files UNION ALL SELECT 'file_references' as table_name, COUNT(*) as file_count, SUM(file_size) as total_size, AVG(file_size) as avg_size, MAX(file_size) as max_size, MIN(file_size) as min_size FROM file_references """)storage_stats=cur.fetchall()returnstorage_statsexceptExceptionase:self.logger.error(f"Error getting storage statistics: {e}")return[]finally:conn.close()defget_large_object_usage(self):"""Get large object usage statistics."""conn=psycopg2.connect(**self.conn_params)try:withconn.cursor()ascur:cur.execute(""" SELECT schemaname, tablename, indexname, idx_scan, idx_tup_read, idx_tup_fetch, pg_size_pretty(pg_relation_size(indexrelid)) as index_size FROM pg_stat_user_indexes WHERE tablename IN ('documents', 'large_files', 'file_references') ORDER BY idx_scan DESC """)usage_stats=cur.fetchall()returnusage_statsexceptExceptionase:self.logger.error(f"Error getting large object usage: {e}")return[]finally:conn.close()defget_file_integrity_status(self):"""Get file integrity status."""conn=psycopg2.connect(**self.conn_params)try:withconn.cursor()ascur:cur.execute(""" SELECT 'documents' as table_name, COUNT(*) as total_files, COUNT(*) FILTER (WHERE checksum IS NOT NULL) as files_with_checksum, COUNT(*) FILTER (WHERE checksum IS NULL) as files_without_checksum FROM documents UNION ALL SELECT 'large_files' as table_name, COUNT(*) as total_files, COUNT(*) FILTER (WHERE checksum IS NOT NULL) as files_with_checksum, COUNT(*) FILTER (WHERE checksum IS NULL) as files_without_checksum FROM large_files """)integrity_status=cur.fetchall()returnintegrity_statusexceptExceptionase:self.logger.error(f"Error getting file integrity status: {e}")return[]finally:conn.close()defgenerate_large_object_report(self):"""Generate comprehensive large object report."""storage_stats=self.get_storage_statistics()usage_stats=self.get_large_object_usage()integrity_status=self.get_file_integrity_status()report={'report_timestamp':datetime.now().isoformat(),'storage_statistics':storage_stats,'usage_statistics':usage_stats,'integrity_status':integrity_status}returnreport# Usageif__name__=="__main__":monitor=LargeObjectMonitor({'host':'localhost','database':'production','user':'monitor_user','password':'monitor_password'})report=monitor.generate_large_object_report()print(json.dumps(report,indent=2))
-- 1. Create large object tablesCREATETABLEdocuments(idSERIALPRIMARYKEY,filenameVARCHAR(255)NOTNULL,content_typeVARCHAR(100)NOTNULL,file_sizeBIGINTNOTNULL,contentBYTEA,created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);-- 2. Create large object functionsCREATEORREPLACEFUNCTIONstore_large_object(p_filenameVARCHAR(255),p_content_typeVARCHAR(100),p_file_dataBYTEA)RETURNSINTEGERAS$$-- Function implementation here$$LANGUAGEplpgsql;-- 3. Store filesSELECTstore_large_object('document.pdf','application/pdf',file_data);-- 4. Retrieve filesSELECT*FROMdownload_file(1);